Java ThreadLocal解析
簡介
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永遠無法回收,造成記憶體泄漏。
參考資料
//www.cnblogs.com/dennyzhangdd/p/7978455.html ThreadLocal終極源碼剖析
//liwx2000.iteye.com/blog/1774169 ThreadLocal會記憶體溢出嗎
//www.jianshu.com/p/cdb2ea3792b5 深入理解Java弱引用