ThreadLocal是什么
ThreadLocal是一個(gè)關(guān)于創(chuàng)建線程局部變量的類。
通常情況下锌奴,我們創(chuàng)建的變量是可以被任何一個(gè)線程訪問并修改的兽狭。而使用ThreadLocal創(chuàng)建的變量只能被當(dāng)前線程訪問憾股,其他線程則無法訪問和修改鹿蜀。
本文需要解決一下幾個(gè)問題
每個(gè)線程的變量副本是存儲(chǔ)在哪里的箕慧?
變量副本是怎么從共享的那個(gè)變量賦值出來的?源碼中的threadlocal的初始值是什么時(shí)機(jī)設(shè)置的茴恰?
ThreadLocal是如何實(shí)現(xiàn)了多個(gè)線程之間每個(gè)線程一個(gè)變量副本的颠焦?它是如何實(shí)現(xiàn)共享變量的。
ThreadLocal機(jī)制主要由Entry往枣、ThreadLocalMap伐庭、Thread、ThreadLocal這四個(gè)類相互協(xié)作實(shí)現(xiàn)的分冈。
我們來看set()方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
這個(gè)方法用當(dāng)前線程t圾另,去獲取實(shí)體map,并set雕沉,如果沒有則Creat
我們?cè)賮砜?strong>get()方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
同set方法差不多
我們來看看**ThreadLocalMap **
//初始化大小
private static final int INITIAL_CAPACITY = 16;
//容器為數(shù)組
private Entry[] table;
Entry
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry的定義很簡(jiǎn)單集乔,它擴(kuò)展自ThreadLocal類型的WeakReference類,是一個(gè)key-value對(duì)類坡椒。key是ThreadLocal對(duì)象的弱引用扰路,value是線程的內(nèi)部變量。
Entry使用弱引用作為key目的是倔叼,希望在外部不再需要訪問ThreadLocal對(duì)象時(shí)可以讓GC盡快地回收對(duì)象汗唱,而不必等到線程結(jié)束后。
當(dāng)GC回收ThreadLocal對(duì)象后丈攒,再通過Entry.get()獲取ThreadLocal對(duì)象時(shí)返回null哩罪,這使得內(nèi)部能夠感知什么時(shí)候不需要再持有對(duì)value的引用,從而釋放Entry對(duì)象的引用巡验,進(jìn)而釋放value的引用识椰,這時(shí)如果value在外部沒有任何引用的話(通常你不應(yīng)該在外部持有對(duì)value的引用),隨后被GC回收深碱。這種感知和釋放的行為發(fā)生在ThreadLocal的get腹鹉、set、remove操作時(shí)敷硅。
通常在Java的世界里功咒,我們不需要關(guān)系對(duì)象的釋放,大部分情況下GC會(huì)自動(dòng)幫我們回收绞蹦。
但是如果使用ThreadLocal不當(dāng)力奋,是有可能導(dǎo)致內(nèi)存泄漏的。
ThreadLocal釋放內(nèi)部變量通常在以下時(shí)機(jī):
線程結(jié)束后
顯式調(diào)用remove
在調(diào)用get幽七、set時(shí)景殷,如果探測(cè)到ThreadLocal對(duì)象的弱引用對(duì)象get返回null順便釋放。
所以,如果線程存活的生命周期很長(zhǎng)猿挚,特別是和進(jìn)程一樣長(zhǎng)的話咐旧,就要特別注意防止ThreadLocal引入內(nèi)存泄漏的風(fēng)險(xiǎn),在不需要再使用某個(gè)線程內(nèi)部變量時(shí)記得顯式調(diào)用remove清理掉绩蜻。