網(wǎng)絡(luò)營(yíng)銷(xiāo)具體推廣方案seo查詢(xún)網(wǎng)站
private ThreadLocal threadLocal = new ThreadLocal();
threadLocal.set(0);
(int) threadLocal.get();
上面三行代碼分別是定義、賦值和取值。
介紹:
我們只需要實(shí)例化對(duì)象一次,并且也不需要知道它是被哪個(gè)線(xiàn)程實(shí)例化。雖然所有的線(xiàn)程都能訪(fǎng)問(wèn)到這個(gè)ThreadLocal實(shí)例,但是每個(gè)線(xiàn)程卻只能訪(fǎng)問(wèn)到自己通過(guò)調(diào)用ThreadLocal的set()方法設(shè)置的值。即使是兩個(gè)不同的線(xiàn)程在同一個(gè)ThreadLocal對(duì)象上設(shè)置了不同的值,他們?nèi)匀粺o(wú)法訪(fǎng)問(wèn)到對(duì)方的值。
需要框架源碼的朋友可以看我個(gè)人簡(jiǎn)介聯(lián)系我,推薦分布式架構(gòu)源碼。?
各個(gè)線(xiàn)程賦值讀取互補(bǔ)干擾的原理:
源碼中有一個(gè)ThreadLoalMap類(lèi)型的東西,理解成map類(lèi)型即可。
詳解一下取值過(guò)程,調(diào)用ThreadLocal的get方法時(shí),會(huì)先調(diào)用getMap(t)獲取到ThreadLoalMap的集合,其中參數(shù)t為當(dāng)前線(xiàn)程(Thread.currentThread()),getMap方法表示每個(gè)線(xiàn)程對(duì)象中都維護(hù)有這么個(gè)ThreadLoalMap對(duì)象集合。
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;
}
然后,調(diào)用該集合的getEntry方法,參數(shù)就是ThreadLocal對(duì)象本身,ThreadLocal的hash值和table.length構(gòu)成了Entry的鍵。
private Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);
}
總結(jié)下:
每個(gè)線(xiàn)程都維護(hù)有一個(gè)ThreadLocalMap對(duì)象集合,他的鍵就是ThreadLocal對(duì)象的hash(相當(dāng)于這么個(gè)東西),這樣A、B兩個(gè)線(xiàn)程就會(huì)各自維護(hù)一個(gè)ThreadLocalMap對(duì)象集合,共用一個(gè)ThreadLocal對(duì)象的hash值當(dāng)作其中的鍵,就相當(dāng)于A(yíng)線(xiàn)程的ThreadLocalMap有共用的hash鍵,B線(xiàn)程的ThreadLocalMap也有一個(gè)共用的hash鍵。這樣就不會(huì)沖突了,有點(diǎn)繞,多查資料多理解。
InheritableThreadLocal其中還有這么個(gè)東西,InheritableThreadLocal類(lèi)是ThreadLocal類(lèi)的子類(lèi)。ThreadLocal中每個(gè)線(xiàn)程擁有它自己的值,與ThreadLocal不同的是,InheritableThreadLocal允許一個(gè)線(xiàn)程以及該線(xiàn)程創(chuàng)建的所有子線(xiàn)程都可以訪(fǎng)問(wèn)它保存的值。相當(dāng)于一個(gè)類(lèi)定義成protected。
1. 每個(gè)Thread實(shí)例內(nèi)部,有二個(gè)ThreadLocalMap的K-V容器實(shí)例(分別對(duì)應(yīng)threadLocals及inheritableThreadLocals), 容器的元素?cái)?shù)量,即為T(mén)hread實(shí)例里的ThreadLocal實(shí)例個(gè)數(shù)
2. ThreadLocalMap里的每個(gè)Entry的Key與ThreadLocal實(shí)例的HashCode相關(guān)(這樣,多個(gè)ThreadLocal實(shí)例就不會(huì)搞混)
3. 每個(gè)ThreadLocal實(shí)例使用set賦值時(shí),實(shí)際上是在ThreadLocalMap容器里,添加(或更新)一條Entry信息
4. 每個(gè)ThreadLocal實(shí)例使用get取值時(shí),從ThreadLocalMap里根據(jù)key取出value 。
關(guān)于內(nèi)存泄漏問(wèn)題:
通過(guò)之前的分析已經(jīng)知道,當(dāng)使用ThreadLocal保存一個(gè)value時(shí),會(huì)在ThreadLocalMap中的數(shù)組插入一個(gè)Entry對(duì)象,按理說(shuō)key-value都應(yīng)該以強(qiáng)引用保存在Entry對(duì)象中,但在ThreadLocalMap的實(shí)現(xiàn)中,key被保存到了WeakReference對(duì)象中,源碼中是繼承WeakReference對(duì)象了。
static class Entry extends WeakReference<ThreadLocal<?>>
ThreadLocal在ThreadLocalMap中是以一個(gè)弱引用身份被Entry中的Key引用的,因此如果ThreadLocal沒(méi)有外部強(qiáng)引用來(lái)引用它,那么ThreadLocal會(huì)在下次JVM垃圾收集時(shí)被回收。這個(gè)時(shí)候就會(huì)出現(xiàn)Entry中Key已經(jīng)被回收,出現(xiàn)一個(gè)null Key的情況,外部讀取ThreadLocalMap中的元素是無(wú)法通過(guò)null Key來(lái)找到Value的。因此如果當(dāng)前線(xiàn)程的生命周期很長(zhǎng),一直存在,那么其內(nèi)部的ThreadLocalMap對(duì)象也一直生存下來(lái),這些null key就存在一條強(qiáng)引用鏈的關(guān)系一直存在:Thread --> ThreadLocalMap-->Entry-->Value,這條強(qiáng)引用鏈會(huì)導(dǎo)致Entry不會(huì)回收,Value也不會(huì)回收,但Entry中的Key卻已經(jīng)被回收的情況,造成內(nèi)存泄漏。
但是JVM團(tuán)隊(duì)已經(jīng)考慮到這樣的情況,并做了一些措施來(lái)保證ThreadLocal盡量不會(huì)內(nèi)存泄漏:在ThreadLocal的get()、set()、remove()方法調(diào)用的時(shí)候會(huì)清除掉線(xiàn)程ThreadLocalMap中所有Entry中Key為null的Value,并將整個(gè)Entry設(shè)置為null,利于下次內(nèi)存回收。