我对ThreadLocal的理解
聲明:小弟菜狗一個。對ThreadLocal的描寫敘述和理解難免有所偏差
? ? ? ? 近期由于須要深入的了解android的handler消息機制而去查看了Looper的源代碼。眾所周知在主線程中是不須要在程序猿在代碼新建一個Looper對象的,由于在主線程創建時它就被創建出來了。所以就好奇它是怎么被創建出來的然后發現它跟ThreadLocal 有關于是便查看了該類的一些資料,但還是不太理解。于是便嘗試自己去看一下源代碼,然后就有了對ThreadLocal一個又一次的認識。先貼出Looper的代碼:
? ? ? ??
private Looper() {//MessageQueue對象會隨Looper對象的創建而創建mQueue = new MessageQueue();mRun = true;mThread = Thread.currentThread();}以下是Looper的代碼,從代碼中看出sThreadLocal是Looper的成員變量。它被new出來了。
當我第一次看到此代碼的時候便產生了一個疑問,印象中不是說ThreadLocal對象都會綁定到一個線程中去的嗎,若創建對象那么怎樣確定它綁定到哪一個線程中呢(到后來我發現我的這樣的想法是不正確的)。于是我便查看了ThreadLocal的代碼。首先由于prepare調用到ThreadLocal的set方法。以下先查看下該方法的實現
public class Looper {private static final boolean DEBUG = false;private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;// sThreadLocal.get() will return null unless you've called prepare().private static final ThreadLocal sThreadLocal = new ThreadLocal();<span style="font-family: Arial, Helvetica, sans-serif;">? ?</span>//該方法事實上就是將一個Looper對象設置進ThreadLocal的一個map中public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper());}public void set(T value) { Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);//ThreadLocalMap是ThreadLocal的內部類if (map != null)map.set(this, value);elsecreateMap(t, value);}由ThreadLocal的方法不難看出set方法設置的值最后會與本ThreadLocal對象湊成一個鍵值對存放到它新建的ThreadLocalMap對象中的。
此時會注意到兩個方法getMap(ThreadLocal tl,T t)和createMap(Thread t, T t)。
通過方法名就不難得出此雙方法是跟ThreadLocalMap對象的獲取和創建有關。
以下先觀察ThreadLocal類中createMap方法的代碼
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}通過代碼能夠知道此方法將一個新建的“鍵值對”為本ThreadLocal對象和要設置的value值的ThreadLocalMap對象賦值給當前線程的threadLocals變量。接下來查看Thread的代碼。 /* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;可見它是Thread的一個成員變量。至此當前線程的threadLocals就不為空并且是不會再被改變(由于從ThreadLocal的set方法中每一次在設置當前threadLocals的值之前都會先推斷該對象是否為null)。
通過觀察這一系列的代碼能夠了解到事實上在每個線程中都會有一個ThreadLocal.ThreadLocalMap變量,它與Map有點類似用于存放鍵值對,只是它的鍵是ThreadLocal對象,所以一個ThreadLocal對象僅僅能在它所在線程的ThreadLocal.ThreadLocalMap對象對象中存放有自己是key的一個值。事實上此時又會產生一個疑問這種以ThreadLocal
為key的鍵值對存放到Thread對象中的ThreadLocal.ThreadLocalMap中有什么意義呢?由于當我們失去了ThreadLocal對象之后就不能取出在線程中以該ThreadLocal的相應值。
事實上通過觀察Looper的代碼不難看出它的ThreadLocal sThreadLocal對象是一個靜態變量。因此全部的Looper對象都在“共用”一個ThreadLocal 對象。因此確保了不同Looper的Looper.prepare方法在同一個線程的ThreadLocal.ThreadLocalMap中相應的值是一樣的,這確保了一個線程中僅僅有一個Looper對象存放在當前線程的ThreadLocal.ThreadLocalMap中。
下面是Message、Message Queue、Handler、Looper類之間的大概的聯系。
####?Handler消息機制
?
>?####?Message?消息
Message msg = Message.obtain()Message msg = new Message()//獲取Message類的兩種方式>?####?Handler?
new Handler(){ handlerMessage(Message msg){ // 處理消息 } }>?####?Handler的構造方法:
?
public Handler() {... // 獲取loopermLooper = Looper.myLooper();//事實上就是在本線程的ThreadLocal.Map中取looper對象if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = null;}public static Looper myLooper() {return sThreadLocal.get();}
>?####?主線程設置Looper。在ActivityThread類里面
public static final void main(String[] args) {.... // 1.主線程創建Looper Looper.prepareMainLooper();if (sMainThreadHandler == null) {sMainThreadHandler = new Handler();}ActivityThread thread = new ActivityThread();thread.attach(false);if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}Looper.loop();>?####?Looper
public static final void prepare() {//若在調此方法時本線程(非主線程)中不存在looper對象則會創建一個looper對象存放在線程的ThreadLocalMap對象中if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}//?3、在主線程中設置Looper,?new?Looper()里面創建了一個MessageQueue
sThreadLocal.set(new Looper());public static final void prepareMainLooper() {// 2、調用prepareprepare();setMainLooper(myLooper());if (Process.supportsProcesses()) {myLooper().mQueue.mQuitAllowed = false;}}>?####?主線程調用Looper.loop()方法,主線程就會堵塞,是一個死循環。使用管道(Pipe),是Linux中的一種進程間通信方式,使用了特殊的文件,有兩個文件描寫敘述符(一個是讀取,一個是寫入)
?
>?####?應用場景;主進程拿著讀取描寫敘述符等待讀取,沒有內容時就堵塞,還有一個進程拿寫入描寫敘述符去寫內容,喚醒主進程,主進程拿著讀取描寫敘述符讀取到內容。繼續運行。
?
>?####?Handler應用場景:Handler在主線程中創建,Looper會在死循環里等待取消息,1、沒取到。就堵塞,2、一旦被子線程喚醒,取到消息。就把Message交給Handler處理。
子線程用Handler去發送消息。拿寫入描寫敘述符去寫消息,喚醒主線程。
public static final void loop() {...while (true) { // 取消息。假設沒有消息。就堵塞Message msg = queue.next(); // might block...msg.target.dispatchMessage(msg);... }}>?####?Handler發送消息代碼
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {.... // 把Message的target置為當前發送的Handler,以便Looper取到message后依據target把message分發給正確的Handler msg.target = this; // 往隊列里面加入Messagesent = queue.enqueueMessage(msg, uptimeMillis);....}>?####?MessageQueue.enqueueMessage?代碼
final boolean enqueueMessage(Message msg, long when) {...Message p = mMessages;if (p == null || when == 0 || when < p.when) { // 當前發送的message須要立即被處理調。needWake喚醒狀態置truemsg.next = p;mMessages = msg;needWake = mBlocked; // new head, might need to wake up} else { // 當前發送的message被排隊到其它message的后面。needWake喚醒狀態置falseMessage prev = null;while (p != null && p.when <= when) {prev = p;p = p.next;}msg.next = prev.next;prev.next = msg;needWake = false; // still waiting on head, no need to wake up}} // 是否喚醒主線程if (needWake) {nativeWake(mPtr);}return true;> #### Handler.dispatchMessage方法public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}} // 把Message交給Handler處理handleMessage(msg);}}? ? ? ?
轉載于:https://www.cnblogs.com/liguangsunls/p/6880337.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的我对ThreadLocal的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 适合win7的python版本_Pyth
- 下一篇: postgresql数据库安装及简单操作