ThreadLocal使用場景有哪些欣福?
推薦學(xué)習(xí):必刷的30萬面試題:巧用弱引用解決ThreadLocal內(nèi)存泄漏!
Thread類中有兩個(gè)變量threadLocals和inheritableThreadLocals焦履,二者都是ThreadLocal內(nèi)部類ThreadLocalMap類型的變量拓劝,我們通過查看內(nèi)部內(nèi)ThreadLocalMap可以發(fā)現(xiàn)實(shí)際上它類似于一個(gè)HashMap。在默認(rèn)情況下嘉裤,每個(gè)線程中的這兩個(gè)變量都為null:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
只有當(dāng)線程第一次調(diào)用ThreadLocal的set或者get方法的時(shí)候才會(huì)創(chuàng)建他們郑临。
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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
除此之外,每個(gè)線程的本地變量不是存放在ThreadLocal實(shí)例中屑宠,而是放在調(diào)用線程的ThreadLocals變量里面牧抵。也就是說,ThreadLocal類型的本地變量是存放在具體的線程空間上侨把,其本身相當(dāng)于一個(gè)裝載本地變量的載體犀变,通過set方法將value添加到調(diào)用線程的threadLocals中,當(dāng)調(diào)用線程調(diào)用get方法時(shí)候能夠從它的threadLocals中取出變量秋柄。如果調(diào)用線程一直不終止获枝,那么這個(gè)本地變量將會(huì)一直存放在他的threadLocals中,所以不使用本地變量的時(shí)候需要調(diào)用remove方法將threadLocals中刪除不用的本地變量,防止出現(xiàn)內(nèi)存泄漏骇笔。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocal內(nèi)存泄露問題如何避免省店?
每個(gè)Thread都有一個(gè)ThreadLocal.ThreadLocalMap的map,該map的key為ThreadLocal實(shí)例笨触,它為一個(gè)弱引用懦傍,我們知道弱引用有利于GC回收。當(dāng)ThreadLocal的key == null時(shí)芦劣,GC就會(huì)回收這部分空間粗俱,但是value卻不一定能夠被回收,因?yàn)樗€與Current Thread存在一個(gè)強(qiáng)引用關(guān)系虚吟,如下
由于存在這個(gè)強(qiáng)引用關(guān)系寸认,會(huì)導(dǎo)致value無法回收签财。如果這個(gè)線程對象不會(huì)銷毀那么這個(gè)強(qiáng)引用關(guān)系則會(huì)一直存在,就會(huì)出現(xiàn)內(nèi)存泄漏情況偏塞。所以說只要這個(gè)線程對象能夠及時(shí)被GC回收唱蒸,就不會(huì)出現(xiàn)內(nèi)存泄漏。如果碰到線程池灸叼,那就更坑了神汹。 那么要怎么避免這個(gè)問題呢? 在前面提過古今,在ThreadLocalMap中的setEntry()慎冤、getEntry(),如果遇到key == null的情況沧卢,會(huì)對value設(shè)置為null蚁堤。當(dāng)然我們也可以顯示調(diào)用ThreadLocal的remove()方法進(jìn)行處理。 下面再對ThreadLocal進(jìn)行簡單的總結(jié):
- ThreadLocal 不是用于解決共享變量的問題的但狭,也不是為了協(xié)調(diào)線程同步而存在披诗,而是為了方便每個(gè)線程處理自己的狀態(tài)而引入的一個(gè)機(jī)制。這點(diǎn)至關(guān)重要立磁。
- 每個(gè)Thread內(nèi)部都有一個(gè)ThreadLocal.ThreadLocalMap類型的成員變量呈队,該成員變量用來存儲(chǔ)實(shí)際的ThreadLocal變量副本。
- ThreadLocal并不是為線程保存對象的副本唱歧,它僅僅只起到一個(gè)索引的作用宪摧。它主要是為每一個(gè)線程隔離一個(gè)類的實(shí)例,這個(gè)實(shí)例的作用范圍僅限于線程內(nèi)部颅崩。