ThreadLocal源碼解析

一、使用場(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ù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容