小葵花妈妈课堂开课了:《ThreadLocal 浅析》
ThreadLocal
先看一下一下官方的解釋:
/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* get or set method) has its own, independently initialized
* copy of the variable. ThreadLocal instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
**/
我自己翻譯了一下:
ThreadLocal類提供了線程本地局部變量,這些局部變量區(qū)別于其他變量是因?yàn)?#xff0c;每一個(gè)線程都會(huì)有它自己的值,這個(gè)變量會(huì)在每一個(gè)線程中獨(dú)立進(jìn)行初始化。ThreadLocal實(shí)例通常都是私有靜態(tài)的,這樣做是希望將狀態(tài)與線程聯(lián)系起來。
/** Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the ThreadLocal
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
**/
每一個(gè)線程都隱士的引用著他自己的線程本地變量的副本,只要線程一直存活并且ThreadLocal實(shí)例可以訪問。線程消失之后,所有的它自己的本地線程實(shí)例靠背對(duì)象都將會(huì)被回收(除非還有其他引用指向著這些副本)。
按照官方這兩段說明,大概意思是說ThreadLocal提供了一種能力:一個(gè)變量在多線程中都具有相同的初值,并且都存在于各自線程中彼此獨(dú)立互不影像、并且只要在同一線程中訪問都是同一個(gè)變量。當(dāng)線程結(jié)束后,該變量也會(huì)被回收。
下面介紹一下ThreadLocal源碼:
/** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */ /** * ThreadLocal依靠線性探測(cè)哈希表附著到每個(gè)Thread. * ThreadLocal對(duì)象都使用threadLocalHashCode來進(jìn)行區(qū)分。 * 這是一個(gè)自定義的哈希值(僅用在ThreadLocalMaps內(nèi)部),用來區(qū)分在 * 同一個(gè)線程中連續(xù)構(gòu)造的多個(gè)ThreadLocal對(duì)象。 */ /** * threadLocalHashCode 在set、get、remove方法中都是將這個(gè)值當(dāng)做key進(jìn)行查找 */ private final int threadLocalHashCode = nextHashCode();public ThreadLocal(); protected T initialValue(); T get(); void set(T value); void remove();下面介紹一下簡單示例用法:
public class ThreadLocalTestActivity extends FragmentActivityRoot {private static final ThreadLocalTest threadLocalTest = new ThreadLocalTest();public static TestBean getTestBean() {TestBean testBean = threadLocalTest.get();if (testBean == null) {testBean = new TestBean();threadLocalTest.set(testBean);}return testBean;}private static class ThreadLocalTestRunable implements Runnable {@Overridepublic void run() {int count = 0;while(count < 10) {getTestBean().testInt += 100;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.printf(Thread.currentThread().getName()+ ": threadLocal=%d\n", getTestBean().testInt);count++;}}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test_thread_local);for( int i = 0;i < 2;i++) {Thread thread = new Thread(new ThreadLocalTestRunable());thread.start();}} }日志如下:
d: fd=88
03-26 20:52:34.354 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=101
03-26 20:52:34.354 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=101
03-26 20:52:34.455 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=201
03-26 20:52:34.455 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=201
03-26 20:52:34.556 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=301
03-26 20:52:34.556 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=301
03-26 20:52:34.656 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=401
03-26 20:52:34.656 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=401
03-26 20:52:34.757 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=501
03-26 20:52:34.757 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=501
03-26 20:52:34.857 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=601
03-26 20:52:34.857 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=601
03-26 20:52:34.957 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=701
03-26 20:52:34.957 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=701
03-26 20:52:35.059 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=801
03-26 20:52:35.060 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=801
03-26 20:52:35.162 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=901
03-26 20:52:35.165 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=901
03-26 20:52:35.264 8163-8592/com.dqs.shangri.testinstrumentation I/System.out: Thread-5: threadLocal=1001
03-26 20:52:35.266 8163-8593/com.dqs.shangri.testinstrumentation I/System.out: Thread-6: threadLocal=1001
可以看到每個(gè)線程都有自己的TestBean 對(duì)象。每個(gè)線程在調(diào)用getTestBean()時(shí)都是返回的同一個(gè)對(duì)象。
為什么會(huì)有如此特性呢,就要分析get和set源碼。
先來介紹一些T get()方法:
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);//第一次調(diào)用時(shí),map為nullif (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null)return (T)e.value;}//1 設(shè)置初始值return setInitialValue(); }/*** Variant of set() to establish initialValue. Used instead* of set() in case user has overridden the set() method.** @return the initial value*/ private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();//2 getMap第一次拿到的仍為nullThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);else {//3 創(chuàng)建mapcreateMap(t, value);}return value; }ThreadLocalMap getMap(Thread t) {return t.threadLocals; }//可復(fù)寫該方法。 protected T initialValue() {return null; }ThreadLocalMap getMap(Thread t) {return t.threadLocals; }void createMap(Thread t, T firstValue) {//4 到這就明朗了,第一次使用的時(shí)候,會(huì)給每個(gè)線程的threadLocals進(jìn)行初始化,//類型為ThreadLocal.ThreadLocalMap//一次調(diào)用的firstValue來自T initialValue();//如果沒有復(fù)寫該方法則為nullt.threadLocals = new ThreadLocalMap(this, firstValue); }上面的第一次get調(diào)用路徑已經(jīng)明了,
首先要拿到當(dāng)前線程Thread,如果當(dāng)前的線程的threadLocals為null,就創(chuàng)建一個(gè)。
其中創(chuàng)建的ThreadLocalMap的官方說法為:
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
構(gòu)造一個(gè)map,使用初始值(firstKey, firstValue)。ThreadLocalMaps 是懶加載的,
當(dāng)我們即將要將entry 壓入到Map中時(shí)才進(jìn)行創(chuàng)建。
也就是說ThreadLocalMap是一種map,通過key-value形式存儲(chǔ)我們給他的值。
也就是說每一個(gè)線程都有自己的ThreadLocalMap,key為ThreadLocal,value為任意Object。
現(xiàn)在先解釋一下ThreadLocalMap
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
ThreadLocalMap 是一個(gè)定制的哈希map,僅適合用來維護(hù)線程本地值。
ThreadLocal 類外不能操作ThreadLocalMap 。
該類僅能在Thread class中使用。
用來解決非常大和長時(shí)間的引用,這些引用在哈希表內(nèi)使用弱引用方式存儲(chǔ)。
然而,自從引用不被使用時(shí),無用的條目會(huì)被刪除僅當(dāng)表的空間將被耗盡的時(shí)候。
散列表 開放定址法 具體介紹請(qǐng)?zhí)D(zhuǎn)此處
散列表解決沖突的辦法
那么回頭來說get方法:
public T get() {Thread t = Thread.currentThread();//再次調(diào)用時(shí) map已經(jīng)創(chuàng)建成功ThreadLocalMap map = getMap(t);if (map != null) {//根據(jù)map的getEntry可知,如果不遇到?jīng)_突,會(huì)很快找到元素,//如果遇到?jīng)_突會(huì)使用開放定址法來找到元素ThreadLocalMap.Entry e = map.getEntry(this);if (e != null)return (T)e.value;}return setInitialValue(); }/*** Remove the entry for key.* 刪除指定key找到的value*/ private void remove(ThreadLocal key) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {if (e.get() == key) {e.clear();expungeStaleEntry(i);return;}} }當(dāng)我們setEntry之后,如果遇到不再使用要主動(dòng)remove。這樣可以達(dá)到性能最優(yōu)。
總結(jié)一下:
ThreadLocal使用在多線程中,并且想要每一個(gè)線程中彼此獨(dú)立。那么使用ThreadLocal就是合適的。
ThreadLocal內(nèi)部使用數(shù)組形式的哈希表,通過開放定址法解決沖突問題。
ThreadLocal 通常要定義成為:
private static ThreadLocal threadLocal;可調(diào)用方法:
protected T initialValue(); //可以重寫該方法,賦值初始值 T get(); //使用該方法獲取值 void set(T value); //設(shè)置value void remove(); //不需要的時(shí)候removesy_dqs@163.com
總結(jié)
以上是生活随笔為你收集整理的小葵花妈妈课堂开课了:《ThreadLocal 浅析》的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HZHOST实现自定义FSO权限的方法及
- 下一篇: 五光十色变色龙的制作