ThreadLocal

直来直往1發表於2024-08-23

先說結論

一個Thread對應一個ThreadLocalMap,一個ThreadLocalMap可儲存多個ThreadLocal為鍵的鍵值對,值為需要執行緒共享的資料,而且多個執行緒可共用一個ThreadLocal物件,在web專案中多有這樣的設計。

原始碼分析

Thread中有一個ThreadLocalMap型別的屬性threadlocals,ThreadLocalMap是ThreadLocal的一個內部類。

1.ThreadLocal的set方法




先獲取當前執行緒t,再執行getMap(t)



其實就是獲取t.threadLocals,若此時getMap(t)為空,則執行createMap(this,value)



若為空,則建立ThreadMap物件並給當前Thread的threadLocals屬性賦值,並在ThreadLocalMap中放入一對鍵值對,其中鍵為當前呼叫方法的ThreaadLocal物件(this),值為set方法傳入的value。
若非空,則直接在該執行緒對應的ThreadLocalMap中放入該鍵值對即可。

2.ThreadLocal的get方法




與set方法類似,先獲取當前Thread對應的ThreadLocalMap,再透過ThreadLocal例項(this)獲取所需值。

3.remove方法

4.ThreadLocal的併發性

由於每一個ThreadLocal都對應了一個ThreadLocalMap,也就是說每一個執行緒都持有一份資料副本,也就不存在資料的併發操作。
Thread中還有一個ThreadLocalMap型別的屬性inheritableThreadLocals。在new Thread()時,在構造方法裡會把父執行緒中的inheritableThreadLocals的ThreadLocalMap內容複製給當前子執行緒的inheritableThreadLocals,也就是說子執行緒會繼承父執行緒中的inheritableThreadLocals對應的ThreadLocalMap,而且是複製一份,子執行緒中的ThreadLocalMap和父執行緒中的ThreadLocalMap中的內容一開始是一樣的,但是後續的修改將互不影響。

5.ThreadLocal的記憶體洩漏問題

ThreadLocaMap中,key對ThreadLocal的引用時弱引用。


5.1 為什麼要對ThreadLocal使用弱引用?

因為ThreadLocal就是做同執行緒資料共享的,若ThreadLocal中不存在物件引用時,ThreadLocal也應該被回收,而由於設計的引用鏈,Thread引用ThreadLocalMap,ThreadLocalMap中的key又引用ThreadLocal,使得ThreadLocal不會被回收。而弱引用的特點為當物件只有弱引用存在時,該物件可以被gc回收。所以對Thread Local使用弱引用能解決此問題。

5.2 引發的記憶體洩漏問題

ThreadLocal為弱引用,這樣就會導致ThreadLocalMap.Entry的key為null,而value是強引用,那麼會導致value引用的物件只有在Thread銷燬時才會被釋放,ThreadLocalMap中也永遠存在一個value非空的永遠不會被訪問到的Entry,引發了記憶體的洩漏。

  • 解決方法:
    程式設計師手動執行remove方法。

5.3 為什麼不把value設定為弱引用?

避免外部持有key的引用來查詢value,而value卻被回收的情況。

相關文章