1.注意事項(xiàng)
- 使用ThreadLocaL時(shí)要注意:為每個(gè)線程分配一個(gè)對(duì)象的工作并不是由ThreadLocal來(lái)完成的惭适,而是需要在應(yīng)用層面保證的。如果在應(yīng)用層為每個(gè)線程都分配了同一個(gè)對(duì)象實(shí)例攻询,那么ThreadLocal也不能保證線程安全。
2.實(shí)現(xiàn)原理
- ThreadLocal的實(shí)現(xiàn)原理:分兩步踏志,第一步是從當(dāng)前線程對(duì)象剔宪,拿到到ThreadLocalMap對(duì)象(是Thread對(duì)象的一個(gè)名為threadLocals的成員變量);第二步是根據(jù)ThreadLocalMap對(duì)象維護(hù)的映射關(guān)系陨溅,由當(dāng)前ThreadLocal對(duì)象(作為key)终惑,得到value。(注意门扇,ThreadLocalMap類(lèi)并沒(méi)有實(shí)現(xiàn)Map接口雹有,但從功能上可以理解為一個(gè)Map)
3.清理工作
- 通過(guò)ThreadLocal的實(shí)現(xiàn)原理,我們可以發(fā)現(xiàn)臼寄,這些變量都是維護(hù)在Thread類(lèi)內(nèi)部的霸奕,這也就意味著只要線程不退出,對(duì)象的引用就一直存在吉拳。
- 而當(dāng)我們使用線程池時(shí)质帅,就意味著當(dāng)前線程未必會(huì)退出,這種情況下留攒,如果我們將一些很大的對(duì)象設(shè)置到ThreadLocal中煤惩,就會(huì)使系統(tǒng)出現(xiàn)內(nèi)存泄漏的可能(這個(gè)對(duì)象已經(jīng)不再有用了,但卻無(wú)法被回收)稼跳。
- 清理ThreadLocal有兩種方式:
1.使用ThreadLocal.remove()方法
- 使用ThreadLocal.remove()方法移除變量盟庞,這就像我們習(xí)慣性地關(guān)閉數(shù)據(jù)庫(kù)連接一樣,如果確定不需要這個(gè)對(duì)象了汤善,那么就應(yīng)該告訴虛擬機(jī)什猖,請(qǐng)把它回收掉票彪,防止內(nèi)存泄漏。
2.手動(dòng)將ThreadLocal對(duì)象置為null
- 手動(dòng)將ThreadLocal對(duì)象置為null不狮,例如
threadLocal=null
降铸,那么這個(gè)ThreadLocal對(duì)應(yīng)的所有線程的局部變量都有可能被回收。 - 之所以可以這樣摇零,這是因?yàn)門(mén)hreadLocalMap的實(shí)現(xiàn)使用了弱引用(ThreadLocalMap非常類(lèi)似于WeakHashMap)推掸。弱引用就是比強(qiáng)引用弱得多的引用。Java虛擬機(jī)在垃圾回收時(shí)驻仅,如果發(fā)現(xiàn)弱引用谅畅,就會(huì)立即回收。ThreadLocalMap內(nèi)部由一系列Entry構(gòu)成噪服,每一個(gè)Entry都是WeakReference<ThreadLocal>:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
- 因此毡泻,雖然這里使用ThreadLocal作為Map的key,但實(shí)際上粘优,它并不真的持有ThreadLocal的引用仇味。而當(dāng)ThreadLocal的外部強(qiáng)引用被回收時(shí),ThreadLocalMap中的key就會(huì)變成null雹顺。當(dāng)系統(tǒng)進(jìn)行ThreadLocalMap 清理 時(shí)(比如將新的變量加入表中丹墨,就會(huì)自動(dòng)進(jìn)行一次清理),就會(huì)自然將這些垃圾數(shù)據(jù)回收:
END
參考資料:《實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì)》