ThreadLocal
- 從JDK1.2版本開始提供了ThreadLocal類,以一種新的思路解決多線程并發(fā)問題。使用ThreadLocal實(shí)例維護(hù)多線程變量時(shí),不會(huì)出現(xiàn)線程安全問題。雖然多個(gè)線程共用一個(gè)ThreadLocal實(shí)例,但在變量的使用上,各線程之間對(duì)變量不可見。
ThreadLocal的使用
ThreadLocal threadLocal = new ThreadLocal();
-
ThreadLocal提供無參的構(gòu)造函數(shù)進(jìn)行實(shí)例創(chuàng)建。在使用方面,該類也僅提供了幾個(gè)簡(jiǎn)單的方法進(jìn)行操作:
- set方法:
threadLocal.set("threadLocal test")對(duì)ThreadLocal進(jìn)行值得設(shè)置。 - get方法:
Object test = threadLocal.get()進(jìn)行值得獲取。 - remove方法:
threadLocal.remove();刪除對(duì)應(yīng)線程中存儲(chǔ)的值。
- set方法:
在JDK1.5版本開始,ThreadLocal提供泛型的支持,在獲取值時(shí)不再需要需要進(jìn)行類型強(qiáng)轉(zhuǎn)。
ThreadLocal<String> threadLocal = new ThreadLocal();
threadLocal.set("threadLocal test");
String test = threadLocal.get();
源碼淺析
- 通過構(gòu)造函數(shù)我們發(fā)現(xiàn),ThreadLocal在構(gòu)造函數(shù)中并沒有做任何操作,所以我們從set方法作為入口進(jìn)行分析。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
通過set方法我們可以看到,ThreadLocal實(shí)際是在其內(nèi)部維護(hù)了一個(gè)ThreadLocalMap實(shí)例進(jìn)行變量的存儲(chǔ),并把該實(shí)例與線程進(jìn)行綁定。在ThreadLocalMap中key的值為this,也就是當(dāng)前線程的ThreadLocal實(shí)例,value為當(dāng)前設(shè)置的值
在get方法中,通過調(diào)用
ThreadLocalMap map = getMap(t);獲取當(dāng)前線程綁定的ThreadLocalMap實(shí)例,并獲取value值。值得注意的是ThreadLocal同一線程只能存儲(chǔ)一個(gè)值,如果需要存儲(chǔ)多個(gè)值,可以考慮封裝成一個(gè)對(duì)象進(jìn)行存儲(chǔ)。
默認(rèn)值
- ThreadLocal通過
protected T initialValue()方法進(jìn)行value的初始化。
protected T initialValue() {
return null;
}
- 通過源碼我們可以看到,該方法直接返回
null。所以在創(chuàng)建實(shí)例后直接調(diào)用get方法時(shí),我們得到的是空值。如果我們需要在ThreadLocal實(shí)例創(chuàng)建的時(shí)候進(jìn)行初始化,可以通過繼承ThreadLocal并重寫initialValue()方法即可。 - 那么
initialValue()方法在何時(shí)進(jìn)行調(diào)用的呢?通過查找源碼我們?cè)趃et方法中看到了該方法的調(diào)用。
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();
- 通過get方法執(zhí)行流程可以看到,當(dāng)ThreadLocal創(chuàng)建實(shí)例的時(shí)候,
ThreadLocalMap map = getMap(t);返回的map值肯定為空,所以執(zhí)行setInitialValue();并返回,我們進(jìn)入該方法:
private T setInitialValue() {
T value = initialValue(); //目標(biāo)代碼
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else createMap(t, value);
return value;
}
從setInitialValue方法實(shí)現(xiàn)可以看到,在該方法中調(diào)用了initialValue返回了我們自定義的返回值,并設(shè)置到ThreadLocalMap實(shí)例中。
通過上面的查看源碼可以發(fā)現(xiàn),ThreadLocal在設(shè)置初始化值時(shí),需要在第一次調(diào)用get方法時(shí)才會(huì)進(jìn)行初始化,而不是在實(shí)例創(chuàng)建的時(shí)候進(jìn)行初始化。
以上即為個(gè)人對(duì)ThreadLocal的簡(jiǎn)單理解。