InheritableThreadLocal

ThreadLocal

在我之前的文章中介紹過什么是ThreadLocal,但是我現(xiàn)在有另一個需求,我想在子線程中獲取到父線程中ThreadLocal的數(shù)據(jù),例如下面的代碼示例:

public class App11 {
    public static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) throws InterruptedException {
        threadLocal.set(1);
        new Thread(()->{
            Integer data = threadLocal.get();
            System.out.printf("%s 獲取到的值:%d\n",Thread.currentThread().getName(),data);
        }).start();
        TimeUnit.SECONDS.sleep(1L);
        System.out.printf("%s 獲取到的值:%d\n",Thread.currentThread().getName(),threadLocal.get());
    }
}

如果了解ThreadLocal的話可以知道最后的打印結(jié)果如下:

Thread-0 獲取到的值:null
main 獲取到的值:1

如果我想在子線程中獲取到父線程放入的值,我該怎么辦呢?最簡單的方法就是將該值傳入到子線程,但是這種方式比較麻煩,有沒有更簡便的方式呢?

InheritableThreadLocal

對于上面所說的情況,我們只需要使用InheritableThreadLocal就能解決,修改代碼如下:

public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();

我這里講ThreadLocal直接改成InheritableThreadLocal,再次運行代碼結(jié)果如下:

Thread-0 獲取到的值:1
main 獲取到的值:1

實現(xiàn)原理

通過上面的方式我們很簡單的就實現(xiàn)了子線程獲取到父線程中的值,那么InheritableThreadLocal是如何做到的呢?我們先看這個類的定義:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    
    protected T childValue(T parentValue) {
        return parentValue;
    }
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

從代碼結(jié)構(gòu)上我們很容易看出它是ThreadLocal的一個子類,同時它重寫了ThreadLocal中的三個方法??吹竭@里的實現(xiàn)你一定在想為什么要重寫這三個方法呢?這里我先解釋一下為什么要重寫getMap方法和createMap方法。
在講ThreadLocal是的時候我們見過,ThreadLocal是通過在Thread中保存ThreadLocal與數(shù)據(jù)的映射關(guān)系存在Thread的變量threadLocals中來實現(xiàn)的,而重寫getMap它返回的是inheritableThreadLocals。簡單的說就是InheritableThreadLocal是通過在Thread中保存InheritableThreadLocal與數(shù)據(jù)的映射關(guān)系存在Thread的變量inheritableThreadLocals中來實現(xiàn)的,這樣做當(dāng)我們在操作inheritableThreadLocals時將不會影響到ThreadLocal中的數(shù)據(jù)。而重寫createMap的原因與getMap類似。

而childValue該方法是在父線程創(chuàng)建子線程,向子線程復(fù)制InheritableThreadLocal變量時使用。而InheritableThreadLocal中的實現(xiàn)對于向子線程復(fù)制值是并沒有做任何改變,如果在上面的例子中,我們想在子線程獲取到父線程的值時乘以10,我們可以繼承InheritableThreadLocal并重寫它的childValue方法即可:

public class App11 {
    //修改成我們自己的InheritableThreadLocal
    public static ThreadLocal<Integer> threadLocal = new MyInheritableThreadLocal();
    public static void main(String[] args) throws InterruptedException {
        threadLocal.set(1);
        new Thread(()->{
            Integer data = threadLocal.get();
            System.out.printf("%s 獲取到的值:%d\n",Thread.currentThread().getName(),data);
        }).start();
        TimeUnit.SECONDS.sleep(1L);
        System.out.printf("%s 獲取到的值:%d\n",Thread.currentThread().getName(),threadLocal.get());
    }
}
//自定義的InheritableThreadLocal
class MyInheritableThreadLocal extends InheritableThreadLocal<Integer>{
    @Override
    protected Integer childValue(Integer parentValue) {
        return parentValue * 10;
    }
}

最后的打印結(jié)果如下:

Thread-0 獲取到的值:10
main 獲取到的值:1

子線程是如何獲取到父線程中的數(shù)據(jù)

在創(chuàng)建線程的構(gòu)造方法中會調(diào)用一個init方法,應(yīng)為該方法較長我截取了部分關(guān)鍵代碼如下:

init方法.png

從上面代碼可以知道,子線程中的inheritableThreadLocals是通過下面的代碼獲取的:

ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)

我們直接繼續(xù)跟蹤ThreadLocal.createInheritedMap的實現(xiàn)直接就是調(diào)用ThreadLocalMap私有的有參構(gòu)造方法,該方法的內(nèi)部實現(xiàn)如下:

ThreadLocalMap私有有參構(gòu)造方法.png

該方法的實現(xiàn)就是將父線程中inheritableThreadLocals的數(shù)據(jù)復(fù)制到子線程的inheritableThreadLocals中,從而實現(xiàn)了我們在子線程中可以獲取到父線程中的值。

總結(jié)

如果明白ThreadLocal是如何實現(xiàn)的,再來理解InheritableThreadLocal的實現(xiàn)原理就很簡單的了。簡單的說就是ThreadLocal是通過將ThreadLocal與數(shù)據(jù)的映射關(guān)系存在Thread的變量threadLocals中來實現(xiàn)的,而InheritableThreadLocal則是將這種關(guān)系存在inheritableThreadLocals中,而在子線程創(chuàng)建時它會將父線程中inheritableThreadLocals的值復(fù)制到子線程的inheritableThreadLocals。

但是InheritableThreadLocal也有一個限制,在實際的開發(fā)中我們很少直接創(chuàng)建線程,一般都是通過線程池的方式來獲取線程。這樣也導(dǎo)致了InheritableThreadLocal在線程池中無法實現(xiàn)這種效果,因為線程池中的線程會重復(fù)利用。如果有這方面的需求,我們可以選擇使用阿里開源的TransmittableThreadLocal來實現(xiàn)。

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

相關(guān)閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    余生動聽閱讀 10,916評論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,497評論 1 3
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,908評論 2 7

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