ThreadLocal即線程局部變數的意思!所以什麼是線程局部變數?這玩意有什麼鳥用?是不是面試被問到了說不出個一二三?今天就來扒一扒這貨的源碼,從根本上了解這貨是乾啥的。
Thread、ThreadLocalMap、Entry三者關係
其實研究下來他的原始碼實現,其實也沒想像的那麼複雜,其最主要有以下幾點:
1、Java可以透過Thread.currentThread()來獲得目前的Thread的實例物件。既然能拿到這Thread物件實例,那麼我們就可以操作該實例(的屬性),例如為該Thread物件設定一個值什麼。
2、每一個Thread物件都有一個ThradLocalMap實例,該實例有一個Entry組成的數組,Entry物件有兩個主要屬性:value和ThreadLocal的弱引用,其中value這個屬性就是值設定給當前線程所持有,也是ThreadLocal的核心屬性:
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
注意Entry繼承自WeakReference,其key就是ThreadLocal物件! (圖1)
結合1和2兩個知識點,我們就可以知道我們拿到Thread對象之後,就可以操控當前線程對象的ThreadLocalMap對象,然後把想要保存的value交給ThreadLocalMap的Entry的value屬性,Thread,ThreadLocalMap,value三者之間的關係可以用下圖表示(圖2):
透過上圖我們可以得到這麼一個結論:一個Thread對象持有一個ThreadLocalMap對象,然後呢,一個ThreadLoalMap對象又包含了多個ThreadLlocal對象及ThreadLocal對象所在線程的value! ! !一言以蔽之: 一個Thread物件可以持有多個ThreadLocal物件的變數值value
那麼ThreadLocal和Thread又有啥關係呢?二者是怎能對value進行讀取的呢?下面就根據原始碼來簡單的分析下。
ThreadLocal和Thread的關聯
先看看ThreadLocal的set方法:
public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程持有的ThreadLocalMap ThreadLocal.ThreadLocalMap map = getMap(t); //将value设置给threadlocalMap if (map != null) map.set(this, value); else createMap(t, value); }
set方法很邏輯很簡單(j結合上圖2看更好理解):
1、透過currentThread方法拿到當前Thread物件
2、取得目前Thread物件的ThreadLoalMap物件
3、將value連同ThreadLocal對象自己組成一個Entry物件保存在
ThreadLoalMap的Entry類型的陣列中。
在來看看ThreadLocal的get方法:
public T get() { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); if (map != null) { //获取与ThreadLocal对象想关联的value ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") //获取值 T result = (T)e.value; return result; } } //为空返回初始化值 return setInitialValue(); }
可以發展get的整體邏輯也很簡單:
1、取得目前Thread物件
2、取得目前Thread物件的ThreadLocalMap物件
3、從ThreadLocalMap取得與ThreadLocal相關聯的Entry對象,具體的就是以ThreadLocal為key取得。
4、取得步驟3的Entry的value屬性,並回傳。
透過整體觀察get與set方法可以得到以下結論:ThreadLocal物件呼叫set方法就是在Thread物件的ThreadLocalMap裡面加上值;ThreadLocal物件呼叫get方法就是從Thread物件的ThreadLocalMap裡面取得值。核心就是操縱Thread物件的ThreadLocalMap物件進行value的讀取與寫入。原理就這麼簡單。
那麼位於不同執行緒的不同ThreadLocal對象,在其他執行緒裡保存值是什麼樣的關係呢?可以透過下圖來清楚的描述出來:
ThreadLocal的使用實例
我們在知道在Android中一個執行緒只有一個Looper對象,那麼怎麼做到的呢?就是ThreadLocal發揮了作用,看看Looper的prepare方法:
//定义一个静态的ThreadLocal变量 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { //一个Thread只能关联一个Looper对象 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
觀察prepare方法可以知道,先透過sThreadLocal的get方法判斷當前線程是否已經擁有了一個Looper對象,如果有就拋出一個異常;如果當前線程還沒有設定Looper對象,則呼叫ThreadLocal的set方法,初始化一個Looper對象交給當前線程:
sThreadLocal.set(new Looper(quitAllowed));
這樣就確保了一個線程只有一個Looper對象。
到此為止,關於ThreadLocal的原理已經基本分析完畢,至於內部是怎麼set和get的,博主並沒有做太細的分析,因為沒必要,了解ThreadLocal的工作原因以及使用場景即可。
以上是ThreadLocal原理淺析的詳細內容。更多資訊請關注PHP中文網其他相關文章!