日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

我对ThreadLocal的理解

發(fā)布時(shí)間:2023/12/20 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我对ThreadLocal的理解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

聲明:小弟菜狗一個(gè)。對(duì)ThreadLocal的描寫敘述和理解難免有所偏差

? ? ? ? 近期由于須要深入的了解android的handler消息機(jī)制而去查看了Looper的源代碼。眾所周知在主線程中是不須要在程序猿在代碼新建一個(gè)Looper對(duì)象的,由于在主線程創(chuàng)建時(shí)它就被創(chuàng)建出來(lái)了。所以就好奇它是怎么被創(chuàng)建出來(lái)的然后發(fā)現(xiàn)它跟ThreadLocal 有關(guān)于是便查看了該類的一些資料,但還是不太理解。于是便嘗試自己去看一下源代碼,然后就有了對(duì)ThreadLocal一個(gè)又一次的認(rèn)識(shí)。先貼出Looper的代碼:

? ? ? ??

private Looper() {//MessageQueue對(duì)象會(huì)隨Looper對(duì)象的創(chuàng)建而創(chuàng)建mQueue = new MessageQueue();mRun = true;mThread = Thread.currentThread();}


以下是Looper的代碼,從代碼中看出sThreadLocal是Looper的成員變量。它被new出來(lái)了。

當(dāng)我第一次看到此代碼的時(shí)候便產(chǎn)生了一個(gè)疑問(wèn),印象中不是說(shuō)ThreadLocal對(duì)象都會(huì)綁定到一個(gè)線程中去的嗎,若創(chuàng)建對(duì)象那么怎樣確定它綁定到哪一個(gè)線程中呢(到后來(lái)我發(fā)現(xiàn)我的這樣的想法是不正確的)。于是我便查看了ThreadLocal的代碼。首先由于prepare調(diào)用到ThreadLocal的set方法。以下先查看下該方法的實(shí)現(xiàn)

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>//該方法事實(shí)上就是將一個(gè)Looper對(duì)象設(shè)置進(jìn)ThreadLocal的一個(gè)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的內(nèi)部類if (map != null)map.set(this, value);elsecreateMap(t, value);}
由ThreadLocal的方法不難看出set方法設(shè)置的值最后會(huì)與本ThreadLocal對(duì)象湊成一個(gè)鍵值對(duì)存放到它新建的ThreadLocalMap對(duì)象中的。

此時(shí)會(huì)注意到兩個(gè)方法getMap(ThreadLocal tl,T t)和createMap(Thread t, T t)。

通過(guò)方法名就不難得出此雙方法是跟ThreadLocalMap對(duì)象的獲取和創(chuàng)建有關(guān)。

以下先觀察ThreadLocal類中createMap方法的代碼

void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
通過(guò)代碼能夠知道此方法將一個(gè)新建的“鍵值對(duì)”為本ThreadLocal對(duì)象和要設(shè)置的value值的ThreadLocalMap對(duì)象賦值給當(dāng)前線程的threadLocals變量。接下來(lái)查看Thread的代碼。

/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;可見(jiàn)它是Thread的一個(gè)成員變量。至此當(dāng)前線程的threadLocals就不為空并且是不會(huì)再被改變(由于從ThreadLocal的set方法中每一次在設(shè)置當(dāng)前threadLocals的值之前都會(huì)先推斷該對(duì)象是否為null)。


通過(guò)觀察這一系列的代碼能夠了解到事實(shí)上在每個(gè)線程中都會(huì)有一個(gè)ThreadLocal.ThreadLocalMap變量,它與Map有點(diǎn)類似用于存放鍵值對(duì),只是它的鍵是ThreadLocal對(duì)象,所以一個(gè)ThreadLocal對(duì)象僅僅能在它所在線程的ThreadLocal.ThreadLocalMap對(duì)象對(duì)象中存放有自己是key的一個(gè)值。事實(shí)上此時(shí)又會(huì)產(chǎn)生一個(gè)疑問(wèn)這種以ThreadLocal

為key的鍵值對(duì)存放到Thread對(duì)象中的ThreadLocal.ThreadLocalMap中有什么意義呢?由于當(dāng)我們失去了ThreadLocal對(duì)象之后就不能取出在線程中以該ThreadLocal的相應(yīng)值。

事實(shí)上通過(guò)觀察Looper的代碼不難看出它的ThreadLocal sThreadLocal對(duì)象是一個(gè)靜態(tài)變量。因此全部的Looper對(duì)象都在“共用”一個(gè)ThreadLocal 對(duì)象。因此確保了不同Looper的Looper.prepare方法在同一個(gè)線程的ThreadLocal.ThreadLocalMap中相應(yīng)的值是一樣的,這確保了一個(gè)線程中僅僅有一個(gè)Looper對(duì)象存放在當(dāng)前線程的ThreadLocal.ThreadLocalMap中。


下面是Message、Message Queue、Handler、Looper類之間的大概的聯(lián)系。

####?Handler消息機(jī)制

?

>?####?Message?消息

Message msg = Message.obtain()Message msg = new Message()//獲取Message類的兩種方式

>?####?Handler?

new Handler(){ handlerMessage(Message msg){ // 處理消息 } }

>?####?Handler的構(gòu)造方法:

?

public Handler() {... // 獲取loopermLooper = Looper.myLooper();//事實(shí)上就是在本線程的ThreadLocal.Map中取looper對(duì)象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();}

>?####?主線程設(shè)置Looper。在ActivityThread類里面

public static final void main(String[] args) {.... // 1.主線程創(chuàng)建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() {//若在調(diào)此方法時(shí)本線程(非主線程)中不存在looper對(duì)象則會(huì)創(chuàng)建一個(gè)looper對(duì)象存放在線程的ThreadLocalMap對(duì)象中if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}

//?3、在主線程中設(shè)置Looper,?new?Looper()里面創(chuàng)建了一個(gè)MessageQueue

sThreadLocal.set(new Looper());public static final void prepareMainLooper() {// 2、調(diào)用prepareprepare();setMainLooper(myLooper());if (Process.supportsProcesses()) {myLooper().mQueue.mQuitAllowed = false;}}

>?####?主線程調(diào)用Looper.loop()方法,主線程就會(huì)堵塞,是一個(gè)死循環(huán)。使用管道(Pipe),是Linux中的一種進(jìn)程間通信方式,使用了特殊的文件,有兩個(gè)文件描寫敘述符(一個(gè)是讀取,一個(gè)是寫入)

?

>?####?應(yīng)用場(chǎng)景;主進(jìn)程拿著讀取描寫敘述符等待讀取,沒(méi)有內(nèi)容時(shí)就堵塞,還有一個(gè)進(jìn)程拿寫入描寫敘述符去寫內(nèi)容,喚醒主進(jìn)程,主進(jìn)程拿著讀取描寫敘述符讀取到內(nèi)容。繼續(xù)運(yùn)行。

?

>?####?Handler應(yīng)用場(chǎng)景:Handler在主線程中創(chuàng)建,Looper會(huì)在死循環(huán)里等待取消息,1、沒(méi)取到。就堵塞,2、一旦被子線程喚醒,取到消息。就把Message交給Handler處理。

子線程用Handler去發(fā)送消息。拿寫入描寫敘述符去寫消息,喚醒主線程。

public static final void loop() {...while (true) { // 取消息。假設(shè)沒(méi)有消息。就堵塞Message msg = queue.next(); // might block...msg.target.dispatchMessage(msg);... }}

>?####?Handler發(fā)送消息代碼

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {.... // 把Message的target置為當(dāng)前發(fā)送的Handler,以便Looper取到message后依據(jù)target把message分發(fā)給正確的Handler msg.target = this; // 往隊(duì)列里面加入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) { // 當(dāng)前發(fā)送的message須要立即被處理調(diào)。needWake喚醒狀態(tài)置truemsg.next = p;mMessages = msg;needWake = mBlocked; // new head, might need to wake up} else { // 當(dāng)前發(fā)送的message被排隊(duì)到其它message的后面。needWake喚醒狀態(tài)置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);}}



? ? ? ?

轉(zhuǎn)載于:https://www.cnblogs.com/liguangsunls/p/6880337.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的我对ThreadLocal的理解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。