ThreadLocal是什么
ThreadLocal是一個(gè)本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對(duì)象做一個(gè)映射,各個(gè)線程之間的變量互不干擾,在高并發(fā)場(chǎng)景下,可以實(shí)現(xiàn)無(wú)狀態(tài)的調(diào)用,特別適用于各個(gè)線程依賴不通的變量值完成操作的場(chǎng)景。
下圖為ThreadLocal的內(nèi)部結(jié)構(gòu)圖

從上面的結(jié)構(gòu)圖,我們已經(jīng)窺見ThreadLocal的核心機(jī)制:
- 每個(gè)Thread線程內(nèi)部都有一個(gè)Map。
- Map里面存儲(chǔ)線程本地對(duì)象(key)和線程的變量副本(value)
- 但是,Thread內(nèi)部的Map是由ThreadLocal維護(hù)的,由ThreadLocal負(fù)責(zé)向map獲取和設(shè)置線程的變量值。
所以對(duì)于不同的線程,每次獲取副本值時(shí),別的線程并不能獲取到當(dāng)前線程的副本值,形成了副本的隔離,互不干擾。
基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
- 項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 視頻教程:https://doc.iocoder.cn/video/
ThreadLocalMap

ThreadLocalMap是ThreadLocal的內(nèi)部類,沒(méi)有實(shí)現(xiàn)Map接口,用獨(dú)立的方式實(shí)現(xiàn)了Map的功能,其內(nèi)部的Entry也獨(dú)立實(shí)現(xiàn)。
和HashMap的最大的不同在于,ThreadLocalMap結(jié)構(gòu)非常簡(jiǎn)單,沒(méi)有next引用,也就是說(shuō)ThreadLocalMap中解決Hash沖突的方式并非鏈表的方式,而是采用線性探測(cè)的方式。(ThreadLocalMap如何解決沖突? )
在ThreadLocalMap中,也是用Entry來(lái)保存K-V結(jié)構(gòu)數(shù)據(jù)的。但是Entry中key只能是ThreadLocal對(duì)象,這點(diǎn)被Entry的構(gòu)造方法已經(jīng)限定死了。
staticclassEntryextendsWeakReference<ThreadLocal>{
/**ThevalueassociatedwiththisThreadLocal.*/
Objectvalue;
Entry(ThreadLocalk,Objectv){
super(k);
value=v;
}
}
注意了!!
Entry繼承自WeakReference(弱引用,生命周期只能存活到下次GC前),但只有Key是弱引用類型的,Value并非弱引用。(問(wèn)題馬上就來(lái)了)
由于ThreadLocalMap的key是弱引用,而Value是強(qiáng)引用。這就導(dǎo)致了一個(gè)問(wèn)題,ThreadLocal在沒(méi)有外部對(duì)象強(qiáng)引用時(shí),發(fā)生GC時(shí)弱引用Key會(huì)被回收,而Value不會(huì)回收。
當(dāng)線程沒(méi)有結(jié)束,但是ThreadLocal已經(jīng)被回收,則可能導(dǎo)致線程中存在ThreadLocalMap的鍵值對(duì),造成內(nèi)存泄露。(ThreadLocal被回收,ThreadLocal關(guān)聯(lián)的線程共享變量還存在)。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
- 項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud
- 視頻教程:https://doc.iocoder.cn/video/
如何避免泄漏
為了防止此類情況的出現(xiàn),我們有兩種手段。
1、使用完線程共享變量后,顯示調(diào)用ThreadLocalMap.remove方法清除線程共享變量;
既然Key是弱引用,那么我們要做的事,就是在調(diào)用ThreadLocal的get()、set()方法時(shí)完成后再調(diào)用remove方法,將Entry節(jié)點(diǎn)和Map的引用關(guān)系移除,這樣整個(gè)Entry對(duì)象在GC Roots分析后就變成不可達(dá)了,下次GC的時(shí)候就可以被回收。
2、JDK建議ThreadLocal定義為private static,這樣ThreadLocal的弱引用問(wèn)題則不存在了。
審核編輯 :李倩
-
變量
+關(guān)注
關(guān)注
0文章
616瀏覽量
29505 -
線程
+關(guān)注
關(guān)注
0文章
509瀏覽量
20826 -
Thread
+關(guān)注
關(guān)注
2文章
93瀏覽量
27469
原文標(biāo)題:ThreadLocal 搭配線程池使用造成內(nèi)存泄漏的原因和解決方案
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
探索LTC3121:高性能同步升壓DC/DC轉(zhuǎn)換器的應(yīng)用與設(shè)計(jì)秘籍
翱捷科技攜手SURGE實(shí)現(xiàn)印尼首個(gè)1.4GHz 5G FWA商用部署
國(guó)家和省發(fā)改委領(lǐng)導(dǎo)蒞臨潤(rùn)芯微科技考察調(diào)研
線材如何錨定磁性元件高端標(biāo)準(zhǔn)
ThreadLocal是什么
評(píng)論