簡介
ThreadLocal 類似區域性變數,解決了單個執行緒維護自己執行緒內的變數值(存、取、刪),讓執行緒之間的資料進行隔離。(InheritableThreadLocal 特例)
這裡涉及三個類,Thread、ThreadLocal、ThreadLocalMap
原始碼解析
- Thread 中有一個 ThreadLocal.ThreadLocalMap 型別的變數 threadLocals。因為ThreadLocalMap變數是跟執行緒繫結的,所以不存在多執行緒共享變數之間的併發問題,所以ThreadLocal也就是執行緒安全的變數。
- ThreadLocalMap 是 ThreadLocal 的一個內部靜態類,沒有繼承java.util.Map,定義了一個Entry[]變數,通過Entry的get()方法作為key,value屬性作為值來實現一個類似Map的操作
- Entry 是 ThreadLocalMap 的一個內部靜態類,繼承WeakReference<ThreadLocal<?>>,並且定義了一個變數value(Object型別)
- ThreadLocal 內部封裝了getMap()、Set()、Get()、Remove()4個核心方法,用於操作ThreadLocalMap
- 通過getMap()獲取每個子執行緒Thread持有自己的ThreadLocalMap例項,因此它們是不存在併發競爭的
- ThreadLocalMap中Entry[]陣列儲存資料,初始化長度16,大於等於3/4閾值,就進行2倍擴容。
- ThreadLocalMap中Entry的key是對ThreadLocal的弱引用,當主執行緒拋棄掉ThreadLocal物件時,垃圾收集器會忽略這個key的引用而清理掉ThreadLocal物件, 防止了記憶體洩漏。
- 雜湊演算法-魔數0x61c88647,利用一定演算法實現了元素的完美雜湊
看原始碼可以得出,set、get、remove操作的都是ThreadLocalMap,key為當前執行緒,value為執行緒區域性變數快取值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
問題
不呼叫remove會記憶體溢位嗎?
大部分場景下是不會的,少數場景才會。
執行時,會在棧中產生兩個引用,指向堆中相應的物件。
可以看到,ThreadLocalMap使用ThreadLocal的弱引用作為key,這樣一來,假設當ThreadLocal ref和ThreadLocal之間的強引用斷開時,即ThreadLocal ref被置為null,下一次GC時,threadLocal物件勢必會被回收。
這樣,ThreadLocalMap中就會出現key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當前執行緒再遲遲不結束的話,比如使用執行緒池,執行緒使用完成之後會被放回執行緒池中,不會被銷燬,這些key為null的Entry的value就會一直存在一條強引用鏈:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠無法回收,造成記憶體洩漏。
參考資料
https://www.cnblogs.com/dennyzhangdd/p/7978455.html ThreadLocal終極原始碼剖析
https://liwx2000.iteye.com/blog/1774169 ThreadLocal會記憶體溢位嗎
https://www.jianshu.com/p/cdb2ea3792b5 深入理解Java弱引用