Java多線程——ThreadLocal詳解
概念:ThreadLocal用於提供線程局部變量,在多線程環境下可以保證各個線程中的變量獨立於其它線程中的變量。
ThreadLocal保證了多線程環境下數據的獨立性。
先來看ThreadLocal是怎樣使用的:
public class ThreadLocalTest {
private static String commStr;
private static ThreadLocal<String> threadStr
= new ThreadLocal<>();
public static void main(String[] args)
throws InterruptedException {
commStr = "main";
threadStr.set("main");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
commStr = "thread";
threadStr.set("thread");
}
});
thread.start();
thread.join();
System.out.println("commStr:"+commStr);
System.out.println("threadStr:"+threadStr.get());
}
}
// 打印結果:
commStr:thread
threadStr:main
ThreadLocal最常見的使用無非就是get()和set()的實現,那麼這些究竟是怎樣實現線程獨立的呢?
簡單的說,每個線程都維護着一個Map集合(ThreadLocalMap),正是由於這個集合的存在,使得線程間可以相互獨立。
ThreadLocal的set()方法
先獲取當前線程,再將該ThreadLocal和要保存的值value存放在當前線程的ThreadLocalMap中(ThreadLocal作爲key,要保存的值value作爲value)。
若當前線程還初始化ThreadLocalMap,則先初始化當前線程的ThreadLocalMap,再執行上面的操作。
ThreadLocal的get()方法
先獲取當前線程,再將該ThreadLocal對象傳入當前線程的ThreadLocalMap中,ThreadLocalMap通過該ThreadLocal(key)查出其所對應的value並返回。
若當前線程的ThreadLocalMap中並沒有key爲該ThreadLocal的鍵值對,或當前線程的ThreadLocalMap根本就還沒有初始化。
①若沒有該key,則將該ThreadLocal和null存入當前線程的ThreadLocalMap中,再將null返回(ThreadLocalMap中value可以爲null)
②若ThreadLocalMap沒有初始化(肯定沒有該key),則先初始化當前線程的ThreadLocalMap,並將該ThreadLocal和null存入ThreadLocalMap,再將null返回。
現在的問題就在於,到底什麼纔是ThreadLocalMap。
每個線程的ThreadLocalMap是屬於線程自己的,ThreadLocalMap中維護的值也是屬於線程自己的。這就保證了ThreadLocal類的變量在每個線程中是獨立的,在多線程環境下不會相互影響。
ThreadLocalMap內部其實維護了一個哈希表來存儲數據,每個鍵值對都是一個Entry對象,該Entry對象用於存儲ThreadLocal對象和value值所構成的鍵值對。ThreadLocalMap中的key與ThreadLocal對象之間採用弱引用的方式
,爲保證ThreadLocal對象與key引用對象能夠被GC正常的回收。
ThreadLocalMap對內存泄漏的處理
1、ThreadLocalMap中的結點Entry的key採用弱引用
(key弱引用ThreadLocal對象),當ThreadLocal對象被回收時,ThreadLocalMap中的key引用對該對象的回收沒什麼影響(該key也會被回收)。
2、此時雖然key被回收後變爲null了(ThreadLocalMap中允許key爲null的存在),ThreadLocalMap中會出現大量key爲null的Entry對象,而此時value並沒有得到釋放,也有可能存在內存泄漏。因此訪問ThreadLocal的set(),get(),remove()方法時,會清除當前線程ThreadLocalMap中所有key==null的value,從而降低內存泄漏的概率。
在使用完ThreadLocal對象後,最好手動調用remove()方法清除數據,防止內存泄漏。