原文:blog.csdn.net/qunqunstyle99/article/details/94717256 ThreadLocal是什麼 ThreadLocal是一個本地線程副本變數工具類。主要用於將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變數互不幹擾,在高併發場景下,可以實現無 ...
原文:blog.csdn.net/qunqunstyle99/article/details/94717256
ThreadLocal是什麼
ThreadLocal是一個本地線程副本變數工具類。主要用於將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變數互不幹擾,在高併發場景下,可以實現無狀態的調用,特別適用於各個線程依賴不通的變數值完成操作的場景。
下圖為ThreadLocal的內部結構圖
從上面的結構圖,我們已經窺見ThreadLocal的核心機制:
- 每個Thread線程內部都有一個Map。
- Map裡面存儲線程本地對象(key)和線程的變數副本(value)
- 但是,Thread內部的Map是由ThreadLocal維護的,由ThreadLocal負責向map獲取和設置線程的變數值。
所以對於不同的線程,每次獲取副本值時,別的線程並不能獲取到當前線程的副本值,形成了副本的隔離,互不幹擾。
推薦一個開源免費的 Spring Boot 最全教程:
ThreadLocalMap
ThreadLocalMap是ThreadLocal的內部類,沒有實現Map介面,用獨立的方式實現了Map的功能,其內部的Entry也獨立實現。
和HashMap的最大的不同在於,ThreadLocalMap結構非常簡單,沒有next引用,也就是說ThreadLocalMap中解決Hash衝突的方式並非鏈表的方式,而是採用線性探測的方式。(ThreadLocalMap如何解決衝突?)
在ThreadLocalMap中,也是用Entry來保存K-V結構數據的。但是Entry中key只能是ThreadLocal對象,這點被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繼承自WeakReference(弱引用,生命周期只能存活到下次GC前),但只有Key是弱引用類型的,Value並非弱引用。(問題馬上就來了)
由於ThreadLocalMap的key是弱引用,而Value是強引用。這就導致了一個問題,ThreadLocal在沒有外部對象強引用時,發生GC時弱引用Key會被回收,而Value不會回收。
當線程沒有結束,但是ThreadLocal已經被回收,則可能導致線程中存在ThreadLocalMap<null, Object>
的鍵值對,造成記憶體泄露。(ThreadLocal被回收,ThreadLocal關聯的線程共用變數還存在)。
如何避免泄漏
為了防止此類情況的出現,我們有兩種手段。
1、使用完線程共用變數後,顯示調用ThreadLocalMap.remove
方法清除線程共用變數;
既然Key是弱引用,那麼我們要做的事,就是在調用ThreadLocal的get()
、set()
方法時完成後再調用remove方法,將Entry節點和Map的引用關係移除,這樣整個Entry對象在GC Roots分析後就變成不可達了,下次GC的時候就可以被回收。
2、JDK建議ThreadLocal定義為private static
,這樣ThreadLocal的弱引用問題則不存在了。
文章參考:
-
www.jianshu.com/p/98b68c97df9b
-
www.cnblogs.com/coshaho/p/5127135.html
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
覺得不錯,別忘了隨手點贊+轉發哦!