一、使用場(chǎng)景
ThreadLocal用于不同線程獲取各自數(shù)據(jù),同一個(gè)線程也可根據(jù)不同的threadlocal對(duì)象獲取到各自的數(shù)據(jù)。
二、源碼解析
ThreadLocal如何實(shí)現(xiàn)不同線程獲取各自數(shù)據(jù)的呢?其實(shí)源碼中使用到ThreadLocal的場(chǎng)景有很多,下面通過(guò)Looper來(lái)分析ThreadLocal的實(shí)現(xiàn)原理。
Android基于消息機(jī)制運(yùn)行,每個(gè)線程都有自己的消息隊(duì)列,通過(guò)Looper不斷循環(huán)獲取消息,進(jìn)行操作。我們?cè)诓煌€程中調(diào)用Looper.myLooper()可以獲取到各自線程的Looper對(duì)象,我們沒(méi)有傳入線程的相關(guān)數(shù)據(jù),那么是如何實(shí)現(xiàn)的呢?
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
很顯然,通過(guò)ThreadLocal的get方法實(shí)現(xiàn),我們繼續(xù)看ThreadLocal
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;
}
在ThreadLocal的get方法中首先獲取了當(dāng)前線程,接著獲取到此線程的ThreadLocalMap對(duì)象,通過(guò)源碼可以得知,它是數(shù)據(jù)存儲(chǔ)類。好了,原來(lái)獲取到的就是每個(gè)線程的數(shù)據(jù)存儲(chǔ)類,當(dāng)然可以實(shí)現(xiàn)不同線程獲取各自的數(shù)據(jù)了。當(dāng)我們獲取到ThreadLocalMap之后,接下來(lái)就要取出其中的value,也就是Looper,這里有兩個(gè)分支。
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
當(dāng)map不為null時(shí),當(dāng)前的ThreadLocal作為key,獲取value,所以(劃重點(diǎn))ThreadLocal不只可以實(shí)現(xiàn)存儲(chǔ)不同線程的數(shù)據(jù),也可以實(shí)現(xiàn)存儲(chǔ)同一線程中不同ThreadLocal對(duì)象的數(shù)據(jù)。
當(dāng)map為null時(shí),首先獲取初識(shí)value,默認(rèn)是null,可以自己重寫initialValue賦值,接著調(diào)用creatMap
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
獲取當(dāng)前線程的ThreadLocalMap存儲(chǔ)對(duì)象,并將當(dāng)前ThreadLocal對(duì)象作為key,賦值初始的value。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
調(diào)用createMap只有兩個(gè)地方,set和setInitialValue,下面我們來(lái)看set
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
同樣,獲取當(dāng)前線程的ThreadLocalMap,若map不為null,則將當(dāng)前ThreadLocal對(duì)象作為key,添加value,若為null,則創(chuàng)建ThreadLocalMap。
我們回到Looper,調(diào)用set的只有一個(gè)地方,就是prepare,主線程即UI線程會(huì)自動(dòng)調(diào)用prepare,將Looper對(duì)象作為value放置到主線程的ThreadLocalMap中;所以這就是為什么子線程使用Looper時(shí)候,先手動(dòng)調(diào)用prepare的原因了。
三、總結(jié)
1、在各自線程中創(chuàng)建其數(shù)據(jù)存儲(chǔ)類ThreadLocalMap,并賦值給線程中的變量threadLocals;獲取數(shù)據(jù)時(shí),直接取出當(dāng)前線程的threadLocals,即獲得此線程的數(shù)據(jù)存儲(chǔ)類。所以不同線程可獲取到各自的數(shù)據(jù)。
2、由于ThreadLocalMap以當(dāng)前ThreadLocal作為key,所以也可實(shí)現(xiàn)同一線程的不同ThreadLocal對(duì)象獲取到各自的數(shù)據(jù)。