ThreadLocal是面試中比較容易碰上的問(wèn)題觅闽,一般會(huì)要求講解它的實(shí)現(xiàn)原理以及存在的問(wèn)題。最近在美團(tuán)的面試中聊到這個(gè)問(wèn)題,雖然去年有看過(guò)并收藏了關(guān)于ThreadLocal的一些文章但目前幾乎忘了干凈(還是實(shí)踐以及理解的不夠沒(méi)有形成記憶 …),所以趁著這個(gè)機(jī)會(huì)好好的總結(jié)記錄一下旋圆。
ThreadLocal的實(shí)現(xiàn)機(jī)制不復(fù)雜,它將自身實(shí)例作為key麸恍,和需要保存的value一起存入到當(dāng)前線程的一個(gè)map當(dāng)中灵巧,代碼可以簡(jiǎn)單的描寫為(當(dāng)然實(shí)際的代碼并不是這樣):
`Thread.currentThread().threadLocals.put(this, value);// threadLocal.set(T)
Thread.currentThread().threadLocals.get(this);// threadLocal.get()`
ThreadLocal實(shí)現(xiàn)中特別值得注意的是1點(diǎn):每個(gè)線程中都保存著一個(gè)threadLocals實(shí)例,該實(shí)例是Map接口的實(shí)現(xiàn) – ThreadLocalMap抹沪。而這個(gè)類實(shí)現(xiàn)的特殊地方在于刻肄,ThreadLocalMap中的Entry中的key類型是WeakReference而非ThreadLocal。為什么ThreadLocalMap中需要使用WeakReference作為key類型融欧,那么首先需要理解WeakReference的意義敏弃。
WeakReference是Java語(yǔ)言規(guī)范中為了區(qū)別直接的對(duì)象引用(程序中通過(guò)構(gòu)造函數(shù)聲明出來(lái)的對(duì)象引用)而定義的另外一種引用關(guān)系。WeakReference標(biāo)志性的特點(diǎn)是:reference實(shí)例不會(huì)影響到被應(yīng)用對(duì)象的GC回收行為(即只要對(duì)象被除WeakReference對(duì)象之外所有的對(duì)象解除引用后噪馏,該對(duì)象便可以被GC回收)麦到,只不過(guò)在被對(duì)象回收之后,reference實(shí)例想獲得被應(yīng)用的對(duì)象時(shí)程序會(huì)返回null欠肾。
理解了WeakReference之后瓶颠,ThreadLocalMap使用它的目的也相對(duì)清晰了:當(dāng)threadLocal實(shí)例可以被GC回收時(shí),系統(tǒng)可以檢測(cè)到該threadLocal對(duì)應(yīng)的Entry是否已經(jīng)過(guò)期(根據(jù)reference.get() == null來(lái)判斷刺桃,如果為true則表示過(guò)期粹淋,程序內(nèi)部稱為stale slots)來(lái)自動(dòng)做一些清除工作,否則如果不清除的話容易產(chǎn)生內(nèi)存無(wú)法釋放的問(wèn)題:value對(duì)應(yīng)的對(duì)象即使不再使用瑟慈,但由于被threadLocalMap所引用導(dǎo)致無(wú)法被GC回收桃移。實(shí)際代碼中,ThreadLocalMap會(huì)在set封豪,get以及resize等方法中對(duì)stale slots做自動(dòng)刪除(set以及get不保證所有過(guò)期slots會(huì)在操作中會(huì)被刪除谴轮,而resize則會(huì)刪除threadLocalMap中所有的過(guò)期slots)。當(dāng)然將threadLocal對(duì)象設(shè)置為null并不能完全避免內(nèi)存泄露對(duì)象吹埠,最安全的辦法仍然是調(diào)用ThreadLocal的remove方法,來(lái)徹底避免可能的內(nèi)存泄露疮装。