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