ThreadLocal內部是個啥玩意?

簡介

ThreadLocal和Synchronized一樣,都是用於處理線程間變量問題;後者有用於等待方式處理變量,前者用多個副本處理對象,時間和空間犧牲;那麼ThreadLocal內部是如何用副本的形式管理的呢?繼續往下看

ThreadLocal一般使用

ThreadLocal<String> t = new ThreadLocal<>();
t.set("name");

一般就是上面的用法,我們看看代碼內部是如何實現的?

public ThreadLocal() {
}

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

set設置值時,會getMap()獲取一個ThreadLocalMap,如果這個map是一個null的話,就需要創建一個ThreadLocalMap,那麼他是如創建的呢?

void createMap(Thread t, T firstValue) {
 	t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
 	table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

在createMap中就是new了一個ThreadLocalMap,但是要注意他把這個map賦值給了t,也就是Thread,所以如果是同一個線程裏面有多個ThreadLocal對象,其實是用同一個ThreadLocalMap
ThreadLocalMap內部結構,是一個Entry數組,通過ThreadLocal的hashcode確定在數組中的位置,然後key是ThreadLocal,value是設置的值;那這個Entry又是什麼結構呢?

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
     Object value;

     Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v;
     }
 }

從上可以知道,Entry的key實質是一個弱引用,也就是Map持有的ThreadLocal是一個弱引用,map的持有並不影響對它的GC,但是value會影響,map被Thread持有,而Thread幾乎可以作爲GC Root的根節點(根搜索法),很有可能造成內存泄漏

比如這種情況就會有內存泄漏,當前Thread線程存活時間很久,並且有多個ThreadLocal成員,如果其中一個ThreadLocal不用了,那麼這個local的value由於Thread存活很久而造成內存泄漏

官方也意識到了這個問題,修改了部分源碼,在對ThreadLocal調用get時候會自動清楚掉Entry數組中key爲null的value,這樣就不會造成內存泄漏了;

但是還是可能會出現,一種就是粗心的程序員沒有去get;另一種是把ThreadLocal設置爲static屬性

總結

綜上所述,同一個線程中的多個ThreadLocal共用一個Entry的Table;不同線程有自己獨立的table;爲了不造成內存泄漏,使用完後get或者remove掉,以防出現內存泄漏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章