點擊關注公眾號,利用碎片時間學習
ThreadLocal是一個本地線程副本變量工具類。主要用於將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變量互不干擾,在高並發場景下,可以實現無狀態的調用,特別適用於各個線程依賴不通的變量值完成操作的場景。
下圖為ThreadLocal的內部結構圖

從上面的結構圖,我們已經窺見ThreadLocal的核心機制:
每個Thread線程內部都有一個Map。
Map裡面存儲線程本地對象(key)和線程的變量副本(value)
但是,Thread內部的Map是由ThreadLocal維護的,由ThreadLocal負責向map獲取和設置線程的變量值。
所以對於不同的線程,每次獲取副本值時,別的線程並不能獲取到當前線程的副本值,形成了副本的隔離,互不干擾。
ThreadLocalMap
ThreadLocalMap是ThreadLocal的內部類,沒有實現Map接口,用獨立的方式實現了Map的功能,其內部的Entry也獨立實現。
和HashMap的最大的不同在於,ThreadLocalMap結構非常簡單,沒有next引用,也就是說ThreadLocalMap中解決Hash衝突的方式並非鍊表的方式,而是採用線性探測的方式。(ThreadLocalMap如何解決衝突?)
在ThreadLocalMap中,也是用Entry來保存K-V結構數據的。但是Entry中key只能是ThreadLocal對象,這點被Entry的構造方法已經限定死了。
staticclassEntryextendsWeakReference<ThreadLocal>{/**ThevalueassociatedwiththisThreadLocal.*/Objectvalue;Entry(ThreadLocalk,Objectv){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的弱引用問題則不存在了。
文章參考:
感謝閱讀,希望對你有所幫助:)
來源:blog.csdn.net/qunqunstyle99/article/details/94717256
推薦
Java面試題寶典
技術內卷群,一起來學習!!
PS:因為公眾號平台更改了推送規則,如果不想錯過內容,記得讀完點一下「在看」,加個「星標」,這樣每次新文章推送才會第一時間出現在你的訂閱列表里。點「在看」支持我們吧!