? ?Threadlocal的作用是提供線程內(nèi)的局部變量,這種變量在線程的生命周期內(nèi)起作用,也就是在一個(gè)線程的不同方法里要被使用到,使用ThreadLocal來存儲(chǔ)這些變量的目的就是為了減少同一個(gè)線程內(nèi)多個(gè)方法之間一些公共變量的傳來傳去,實(shí)現(xiàn)當(dāng)需要使用時(shí)直接從ThreadLocal獲取。如果讓我們自己去設(shè)計(jì)ThreadLocal來達(dá)到這種效果,怎么做?
每個(gè)ThreadLocal類創(chuàng)建一個(gè)Map,然后用線程的id作為map的key,要傳遞的對(duì)象作為map的value?這樣就能達(dá)到各個(gè)線程的值隔離的效果。早期jdk中的ThreadLocal確實(shí)這樣設(shè)計(jì),但后面jdk進(jìn)行了優(yōu)化。
ThreadLocal的構(gòu)造函數(shù)

構(gòu)造函數(shù)啥都沒做。它用initialValue來設(shè)置ThreadLocal的初始值,

該函數(shù)在調(diào)用get函數(shù)時(shí)會(huì)第一次調(diào)用時(shí)被調(diào)用

get方法的流程:
1:首先獲取當(dāng)前線程;
2:根據(jù)當(dāng)前線程獲取一個(gè)map;
3:如果獲取的map不為空,則在該map中以Threadlocal對(duì)象引用作為key來在map中獲取對(duì)應(yīng)的value e 否則轉(zhuǎn)到5 ;
4:如果e不為null,返回e.val ;
5:? Map 為null或者e為空,則通過initialvalue函數(shù)獲取初始值value,然后用ThreadLocal的引用和value作為firstkey和firstvalue創(chuàng)建一個(gè)新的map。
這里的map是ThreadLocal的靜態(tài)內(nèi)部類ThreadLocalMap. 每個(gè)Thread維護(hù)一個(gè) ThreadLocalMap,這個(gè)映射表的key是ThreadLocal實(shí)例本身,value是真正要存儲(chǔ)的對(duì)象。這個(gè)map解決hash沖突采用的線性地址法。
這種設(shè)計(jì)相比早期jdk中那樣設(shè)計(jì)的好處是:
? ? ?1:每個(gè)map的Entry的數(shù)量變少了,之前是 Thread的數(shù)量,現(xiàn)在是ThreadLocal的數(shù)量,之前對(duì)于一個(gè)線程內(nèi)的多個(gè)變量需要多個(gè)map,現(xiàn)在一個(gè)線程的多個(gè)變量對(duì)應(yīng)多個(gè)threadlocal。之前一個(gè)map對(duì)應(yīng)多個(gè)線程,現(xiàn)在一個(gè)map只對(duì)應(yīng)一個(gè)線程,這樣更合理,一個(gè)線程里的所有變量放在一個(gè)map,而不應(yīng)該分散在不同的map!
? ? 2: 當(dāng)Thread銷毀之后對(duì)應(yīng)的ThreadLocalMap也隨之銷毀了,而不必顯示地釋放,能避免內(nèi)存泄漏問題。
ThreadLocal進(jìn)階



需要注意的是,ThreadLocalMap是使用ThreadLocal的弱引用作為key的,如果一個(gè) ThreadLocal沒有外部強(qiáng)引用引用它,那么系統(tǒng)gc時(shí),這個(gè)ThreadLocal必定會(huì)被回收,這樣 ThreadLocalMap中會(huì)出現(xiàn)key為null的Entry,當(dāng)前線程遲遲不結(jié)束的話,這些key為null的 entry的value就會(huì)一直存在強(qiáng)引用,而無法被回收。


ThreadLocalMap的getEntry函數(shù)的流程為:
1: 根據(jù)ThreadLocal的threadlocalhashcode獲取直接索引位置,得到entry e,如果e不為null并且可以相同則直接返回e
2: 如果e為null或者key不一致,則向下一個(gè)位置查詢,如果下一個(gè)位置的key和當(dāng)前需要查詢的key相等,則返回對(duì)應(yīng)的entry,如果key為null,則擦除該位置的entry,繼續(xù)向下一個(gè)位置查詢。
在這個(gè)過程中遇到key為null的entry都會(huì)被擦除,entry內(nèi)的value也就沒有強(qiáng)引用了。將這些key為null的entry擦除有效防止了內(nèi)存泄漏。但是這需要調(diào)用getEntry和set函數(shù),通常很多情況下需要使用者手動(dòng)調(diào)用ThreadLocal的remove函數(shù),remove函數(shù)會(huì)清除對(duì)應(yīng)的value。jdk建議將ThreadLocal變量定義為private static 讓其一直存在著強(qiáng)引用,ThreadLocal也就不會(huì)被回收 。
那么為什么要使用弱引用呢??而不是直接使用強(qiáng)引用?
key如果使用強(qiáng)引用: 引用的threadlocal對(duì)象不再使用了,但是threadlocalmap還持有強(qiáng)引用,如果沒有手動(dòng)刪除,threadlocal不會(huì)被回收,導(dǎo)致entry內(nèi)存泄漏。ThreadLocalMap的生命周期與Thread一樣長,如果都沒有手動(dòng)刪除對(duì)應(yīng)的key,都會(huì)導(dǎo)致內(nèi)存泄漏,使用弱引用可以多一層保障,并且弱引用ThreadLocal不會(huì)內(nèi)存泄漏,對(duì)應(yīng)的value會(huì)在ThreadlocalMap調(diào)用set、get、remove方法時(shí)被清除。