其實(shí)我一直不太理解 threadlocal的設(shè)計(jì),以及 threadlocal 涉及到的內(nèi)存泄漏毕骡。
換個(gè)思路削饵, 如果我不用threadlocal, 我自己如何實(shí)現(xiàn) 線程私有對(duì)象未巫。
最開始窿撬,我們還是會(huì)通過(guò)Thread.currentThread() 獲取當(dāng)前線程,
然后叙凡,我給每個(gè)thread 一個(gè) set 方法劈伴, setPrivateObject(Object v)
這樣,每個(gè)線程都可以存一個(gè) privateObject握爷。
但是通常我們知道跛璧,一個(gè)線程可能創(chuàng)建多個(gè)私有對(duì)象, 那這個(gè)時(shí)候我們的方案該怎么改進(jìn)新啼?
一種粗暴的方法追城,我給每個(gè)Thread 設(shè)置一個(gè) List<Object> pObjects. 每次set 的時(shí)候我就addIntoList。
但是Get()的時(shí)候咋辦燥撞? 總不能遍歷 List座柱,然后根據(jù) Object Type 來(lái)判斷是不是我想要的對(duì)象吧?
這種方法顯然是不行的物舒,那就用map 吧色洞, map的key 是什么? key 就是 Object的類型冠胯。
Map<ClassType, Key> pmap; set(obj.class, val), get(obj.class)
這樣就可以很方便的獲得相應(yīng)的 私有對(duì)象了火诸。
但是存type還是有局限性,為啥荠察? 同一個(gè)類型的私有對(duì)象只能有一個(gè)置蜀。
那我就不要存class 類型奈搜,而是存一個(gè)包含class類型對(duì)象類型。比如我叫 PrivateLocal<T> 對(duì)象盯荤,然后作為key媚污,
這樣就解決了 同一個(gè)類型不能多個(gè)的問(wèn)題。
到這里廷雅,總的方案就是 每個(gè)Thread 里面有一個(gè)map,
這個(gè)map的類型是 Map<PrivateLocal<T>,Object> pMaps;
再去看 thread的源碼京髓,其實(shí)實(shí)現(xiàn)和上面的思路是一樣的航缀,只不過(guò) PrivateLocal 變成了 ThreadLocal<T> 對(duì)象。
并且封裝了其他的方法我們?cè)趤?lái)看堰怨。
假設(shè)使用我們的設(shè)計(jì)方案芥玉,每次 set 或者是 get 的時(shí)候,
我們需要 這樣做备图?
PrivateLoacl<T> type 1 = new PrivateLocal();
Object value = new Object()
Thread.currentThread().set(type1,value);
get 他和時(shí)候灿巧, Thread.currentThread().get(type1);
每次都需要Thread.currentThread() 來(lái)操作。
有沒(méi)有更簡(jiǎn)單的方法揽涮? 有抠藕,就是 threadLocal的現(xiàn)在的做法。
他把set 和 get 方法集成到了 ThreadLocal 里面蒋困。
也就是相當(dāng)于封裝了 一個(gè) set(value)
{
Thread.currentThread().set(this,value)
}
這樣盾似,只要用戶在使用 threadLocal對(duì)象,就不必重復(fù)使用 Thread.currentThread()雪标。
而且這種設(shè)計(jì)也更符合封裝的思想零院, ThreadLocal是一個(gè)線程私有對(duì)像, 哪個(gè)線程調(diào)用 我的set方法村刨,val就屬于哪個(gè)線程告抄,這很直接。
所以源碼設(shè)計(jì)的人還是挺大師的嵌牺。
最后來(lái)看看打洼,為啥有內(nèi)存泄漏。
Thread 對(duì)象里面有一個(gè)
Map<PrivateLocal<T>,Object> pMaps
假設(shè) 這個(gè)thread 不銷毀的話髓梅,這個(gè)pMap 就不會(huì)銷毀拟蜻, 那么 <key, value> 就不會(huì)銷毀。
但是枯饿,我們知道現(xiàn)在 http server等酝锅,很多都是線程池設(shè)計(jì), thread可能很久都不會(huì)銷毀奢方,
而threadlocal 壽命是很短的搔扁,這樣不用的對(duì)象就會(huì)存在爸舒。
怎么辦?
java里面使用了weakReference來(lái)包裝 Threalocal 作為一個(gè) key稿蹲, weakreference 保證 這個(gè)key 在 下一次垃圾回收的時(shí)候被銷毀扭勉。
那么這個(gè)entry 會(huì)變成<null, Value>,
雖然 key 被收回去了,但是entry 還是被pMaps 引用啊苛聘。 這個(gè)還是無(wú)法銷毀涂炎,這就是threadlocal的內(nèi)存泄漏。
怎么辦设哗?
java 推薦的方法就是 在使用完 threadlocal 之后唱捣, 記得調(diào)用 remove方法
而且要在 finally調(diào)用。
這個(gè)是和使用 lock 一樣的网梢。 申請(qǐng)了鎖震缭,一定要記得 release 鎖,而且是在finally 語(yǔ)句里面战虏。