ThreadLocal是什么
ThreadLocal是一個本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變量互不干擾,在高并發場景下,可以實現無狀態的調用,特別適用于各個線程依賴不通的變量值完成操作的場景。
下圖為ThreadLocal的內部結構圖
從上面的結構圖,我們已經窺見ThreadLocal的核心機制:
- 每個Thread線程內部都有一個Map。
- Map里面存儲線程本地對象(key)和線程的變量副本(value)
- 但是,Thread內部的Map是由ThreadLocal維護的,由ThreadLocal負責向map獲取和設置線程的變量值。
所以對于不同的線程,每次獲取副本值時,別的線程并不能獲取到當前線程的副本值,形成了副本的隔離,互不干擾。
基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
- 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 視頻教程:https://doc.iocoder.cn/video/
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
的鍵值對,造成內存泄露。(ThreadLocal被回收,ThreadLocal關聯的線程共享變量還存在)。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
如何避免泄漏
為了防止此類情況的出現,我們有兩種手段。
1、使用完線程共享變量后,顯示調用ThreadLocalMap.remove
方法清除線程共享變量;
既然Key是弱引用,那么我們要做的事,就是在調用ThreadLocal的get()
、set()
方法時完成后再調用remove方法,將Entry節點和Map的引用關系移除,這樣整個Entry對象在GC Roots分析后就變成不可達了,下次GC的時候就可以被回收。
2、JDK建議ThreadLocal定義為private static
,這樣ThreadLocal的弱引用問題則不存在了。
審核編輯 :李倩
-
變量
+關注
關注
0文章
599瀏覽量
28160 -
線程
+關注
關注
0文章
496瀏覽量
19537 -
Thread
+關注
關注
2文章
83瀏覽量
25736
原文標題:ThreadLocal 搭配線程池使用造成內存泄漏的原因和解決方案
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論