在Android的消息機制中,Handler是非常重要的一部分,而完全要理解Handler的機制,首先應(yīng)該理解ThreadLocal,關(guān)于ThreadLocal,見到很多地方叫做線程本地變量,也有些地方叫做線程本地存儲,其實意思差不多。可能很多人都知道ThreadLocal為變量在每個線程中都創(chuàng)建了一個副本,那么每個線程可以訪問自己內(nèi)部的副本變量,這樣的詞容易讓人產(chǎn)生誤解或者迷惑。
首先,從最新的ThreadLocal源碼看,ThreadLocal并未創(chuàng)建任何本地變量,也沒有copy副本的存在,是直接用的Thread對象的成員變量,因此叫做"線程變量幫助類"其實更合適,它的作用就是拿到當前線程對象的Object[] value數(shù)組,然后進行存儲和取值,因為這屬于每個線程的內(nèi)部變量數(shù)組,因此也不存在共享,所以也就沒有線程安全的問題。
先看一個例子:

例子可以看出不同的線程得到的值是不同的,說明ThreadLocal可以使同一個變量在不同的線程里有不同的值,為什么同一個變量在不同的線程的會表現(xiàn)出不同的值呢,源碼說明一切:
先看set方法:


可以看出ThreadLocal的Values引用直接指向Thread的localValues值??聪聀ut()方法的實現(xiàn)。

很好理解,可以簡單看做用單個數(shù)組來實現(xiàn)的簡易hashmap的,hashmap的key是當前ThreadLocal對象的hash值與當前數(shù)組長度的求模運算,存入在數(shù)組的index位置,value就是當前的存入值,這個值總是放在index+1的位置,可以理解為index和index+1這兩個位置就是hashmap的Entry。好像在jdk1.7之前就是用hashmap來實現(xiàn)的,原理都是一樣的。這樣是Thread類更加的輕量化。

通過上面的分析get函數(shù)也很好理解了。先得到當前線程對象的Values對象,然后得到Values中的Object[] table數(shù)組,從數(shù)組中取出值。
ThreadLocal 實例通常建議是用 private static 字段,至于原因想不太清楚。但這不是絕對的,在Android的事件機制Looper中
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
這就不是一個private變量,至于靜態(tài)
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
因為需要靜態(tài)方法獲取Looper對象,所以就必須是靜態(tài)的的吧??吹揭环N說法是設(shè)置static 是因為ThreadLocal支持線程范圍生命周期的變量,所以不屬于類的屬性。不知是否有些牽強。
關(guān)于內(nèi)存泄露的問題
/** Weak reference to this thread local instance. */
private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this);
因為是軟引用持有,所以不會存在內(nèi)存泄露的問題。但確定不需要使用的時候最好調(diào)用remove()方法來釋放內(nèi)存。
簡單總結(jié)
判斷是否需要對資源進行同步的判斷準則是,當前獲取(get)資源是否會有其他線程進行修改(set)或者當前進行修改的資源是否會有其他線程可以獲取。
- synchronized——串行訪問
- volatile——主內(nèi)存刷新,不存在線程副本
- ThreadLocal——線程空間內(nèi)的全局變量