天天看點

面試官:ThreadLocal使用場景有哪些?記憶體洩露問題如何避免?

ThreadLocal使用場景有哪些?

面試官:ThreadLocal使用場景有哪些?記憶體洩露問題如何避免?

image.png

推薦學習:必刷的30萬面試題:巧用弱引用解決ThreadLocal記憶體洩漏!

Thread類中有兩個變量threadLocals和inheritableThreadLocals,二者都是ThreadLocal内部類ThreadLocalMap類型的變量,我們通過檢視内部内ThreadLocalMap可以發現實際上它類似于一個HashMap。在預設情況下,每個線程中的這兩個變量都為null:

ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;      

隻有當線程第一次調用ThreadLocal的set或者get方法的時候才會建立他們。

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();
}

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}      

除此之外,每個線程的本地變量不是存放在ThreadLocal執行個體中,而是放在調用線程的ThreadLocals變量裡面。也就是說,ThreadLocal類型的本地變量是存放在具體的線程空間上,其本身相當于一個裝載本地變量的載體,通過set方法将value添加到調用線程的threadLocals中,當調用線程調用get方法時候能夠從它的threadLocals中取出變量。如果調用線程一直不終止,那麼這個本地變量将會一直存放在他的threadLocals中,是以不使用本地變量的時候需要調用remove方法将threadLocals中删除不用的本地變量,防止出現記憶體洩漏。

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 void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
}      

ThreadLocal記憶體洩露問題如何避免?

每個Thread都有一個ThreadLocal.ThreadLocalMap的map,該map的key為ThreadLocal執行個體,它為一個弱引用,我們知道弱引用有利于GC回收。當ThreadLocal的key == null時,GC就會回收這部分空間,但是value卻不一定能夠被回收,因為他還與Current Thread存在一個強引用關系,如下

  • ThreadLocal 不是用于解決共享變量的問題的,也不是為了協調線程同步而存在,而是為了友善每個線程處理自己的狀态而引入的一個機制。這點至關重要。
  • 每個Thread内部都有一個ThreadLocal.ThreadLocalMap類型的成員變量,該成員變量用來存儲實際的ThreadLocal變量副本。
  • ThreadLocal并不是為線程儲存對象的副本,它僅僅隻起到一個索引的作用。它主要是為每一個線程隔離一個類的執行個體,這個執行個體的作用範圍僅限于線程内部。

繼續閱讀