在Android的消息機(jī)制中信认,Handler是非常重要的一部分材义,而完全要理解Handler的機(jī)制,首先應(yīng)該理解ThreadLocal,關(guān)于ThreadLocal嫁赏,見到很多地方叫做線程本地變量其掂,也有些地方叫做線程本地存儲,其實(shí)意思差不多潦蝇】畎荆可能很多人都知道ThreadLocal為變量在每個(gè)線程中都創(chuàng)建了一個(gè)副本,那么每個(gè)線程可以訪問自己內(nèi)部的副本變量攘乒,這樣的詞容易讓人產(chǎn)生誤解或者迷惑贤牛。
首先,從最新的ThreadLocal源碼看持灰,ThreadLocal并未創(chuàng)建任何本地變量盔夜,也沒有copy副本的存在负饲,是直接用的Thread對象的成員變量堤魁,因此叫做"線程變量幫助類"其實(shí)更合適,它的作用就是拿到當(dāng)前線程對象的Object[] value數(shù)組返十,然后進(jìn)行存儲和取值妥泉,因?yàn)檫@屬于每個(gè)線程的內(nèi)部變量數(shù)組,因此也不存在共享洞坑,所以也就沒有線程安全的問題盲链。
先看一個(gè)例子:
例子可以看出不同的線程得到的值是不同的,說明ThreadLocal可以使同一個(gè)變量在不同的線程里有不同的值,為什么同一個(gè)變量在不同的線程的會(huì)表現(xiàn)出不同的值呢迟杂,源碼說明一切:
先看set方法:
可以看出ThreadLocal的Values引用直接指向Thread的localValues值刽沾。看下put()方法的實(shí)現(xiàn)排拷。
很好理解侧漓,可以簡單看做用單個(gè)數(shù)組來實(shí)現(xiàn)的簡易hashmap的,hashmap的key是當(dāng)前ThreadLocal對象的hash值與當(dāng)前數(shù)組長度的求模運(yùn)算监氢,存入在數(shù)組的index位置布蔗,value就是當(dāng)前的存入值藤违,這個(gè)值總是放在index+1的位置,可以理解為index和index+1這兩個(gè)位置就是hashmap的Entry纵揍。好像在jdk1.7之前就是用hashmap來實(shí)現(xiàn)的顿乒,原理都是一樣的。這樣是Thread類更加的輕量化泽谨。
通過上面的分析get函數(shù)也很好理解了璧榄。先得到當(dāng)前線程對象的Values對象,然后得到Values中的Object[] table數(shù)組吧雹,從數(shù)組中取出值犹菱。
ThreadLocal 實(shí)例通常建議是用 private static 字段,至于原因想不太清楚吮炕。但這不是絕對的腊脱,在Android的事件機(jī)制Looper中
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
這就不是一個(gè)private變量,至于靜態(tài)
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
因?yàn)樾枰o態(tài)方法獲取Looper對象龙亲,所以就必須是靜態(tài)的的吧陕凹。看到一種說法是設(shè)置static 是因?yàn)門hreadLocal支持線程范圍生命周期的變量鳄炉,所以不屬于類的屬性杜耙。不知是否有些牽強(qiáng)。
關(guān)于內(nèi)存泄露的問題
/** Weak reference to this thread local instance. */
private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this);
因?yàn)槭擒浺贸钟蟹鞫ⅲ圆粫?huì)存在內(nèi)存泄露的問題佑女。但確定不需要使用的時(shí)候最好調(diào)用remove()方法來釋放內(nèi)存。
簡單總結(jié)
判斷是否需要對資源進(jìn)行同步的判斷準(zhǔn)則是谈竿,當(dāng)前獲取(get)資源是否會(huì)有其他線程進(jìn)行修改(set)或者當(dāng)前進(jìn)行修改的資源是否會(huì)有其他線程可以獲取团驱。
- synchronized——串行訪問
- volatile——主內(nèi)存刷新,不存在線程副本
- ThreadLocal——線程空間內(nèi)的全局變量