InheritableThreadLocal詳解

1、簡介

在上一篇 ThreadLocal詳解 中,我們詳細介紹了ThreadLocal原理及設(shè)計,從源碼層面上分析了ThreadLocal。但由于ThreadLocal設(shè)計之初就是為了綁定當前線程,如果希望當前線程的ThreadLocal能夠被子線程使用,實現(xiàn)方式就會相當困難(需要用戶自己在代碼中傳遞)。在此背景下,InheritableThreadLocal應運而生。

Inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., User ID, Transaction ID) must be automatically transmitted to any child threads that are created.

2、應用

調(diào)用鏈追蹤:在調(diào)用鏈系統(tǒng)設(shè)計中,為了優(yōu)化系統(tǒng)運行速度,會使用多線程編程,為了保證調(diào)用鏈ID能夠自然的在多線程間傳遞,需要考慮ThreadLocal傳遞問題(大多數(shù)系統(tǒng)會使用線程池技術(shù),這已經(jīng)不僅僅是InheritableThreadLocal能夠解決的了,我會在另外一篇文章中介紹相關(guān)技術(shù)實現(xiàn))。

3、InheritableThreadLocal類

InheritableThreadLocal類重寫了ThreadLocal的3個函數(shù):

    /**
     * 該函數(shù)在父線程創(chuàng)建子線程,向子線程復制InheritableThreadLocal變量時使用
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }
    /**
     * 由于重寫了getMap,操作InheritableThreadLocal時,
     * 將只影響Thread類中的inheritableThreadLocals變量,
     * 與threadLocals變量不再有關(guān)系
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    /**
     * 類似于getMap,操作InheritableThreadLocal時,
     * 將只影響Thread類中的inheritableThreadLocals變量,
     * 與threadLocals變量不再有關(guān)系
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

注意:由于重寫了getMap()和createMap()兩個函數(shù),所以當

4、線程間傳值實現(xiàn)原理

說到InheritableThreadLocal,還要從Thread類說起:

public class Thread implements Runnable {
   ......(其他源碼)
    /* 
     * 當前線程的ThreadLocalMap,主要存儲該線程自身的ThreadLocal
     */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal,自父線程集成而來的ThreadLocalMap,
     * 主要用于父子線程間ThreadLocal變量的傳遞
     * 本文主要討論的就是這個ThreadLocalMap
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    ......(其他源碼)
}

Thread類中包含 threadLocalsinheritableThreadLocals 兩個變量,其中 inheritableThreadLocals 即主要存儲可自動向子線程中傳遞的ThreadLocal.ThreadLocalMap。
接下來看一下父線程創(chuàng)建子線程的流程,我們從最簡單的方式說起:

4.1、用戶創(chuàng)建Thread

Thread thread = new Thread();

4.2、Thread創(chuàng)建

    /**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, null, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     */
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

4.3、Thread初始化

    /**
     * 默認情況下,設(shè)置inheritThreadLocals可傳遞
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
    /**
     * 初始化一個線程.
     * 此函數(shù)有兩處調(diào)用,
     * 1、上面的 init(),不傳AccessControlContext,inheritThreadLocals=true
     * 2、傳遞AccessControlContext,inheritThreadLocals=false
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        ......(其他代碼)

        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

        ......(其他代碼)
    }

可以看到,采用默認方式產(chǎn)生子線程時,inheritThreadLocals=true;若此時父線程inheritableThreadLocals不為空,則將父線程inheritableThreadLocals傳遞至子線程。

4.4、ThreadLocal.createInheritedMap

讓我們繼續(xù)追蹤createInheritedMap:

    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
        /**
         * 構(gòu)建一個包含所有parentMap中Inheritable ThreadLocals的ThreadLocalMap
         * 該函數(shù)只被 createInheritedMap() 調(diào)用.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            // ThreadLocalMap 使用 Entry[] table 存儲ThreadLocal
            table = new Entry[len];

            // 逐一復制 parentMap 的記錄
            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        // 可能會有同學好奇此處為何使用childValue,而不是直接賦值,
                        // 畢竟childValue內(nèi)部也是直接將e.value返回;
                        // 個人理解,主要為了減輕閱讀代碼的難度
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

從ThreadLocalMap可知,子線程將parentMap中的所有記錄逐一復制至自身線程。

5、總結(jié)

InheritableThreadLocal主要用于子線程創(chuàng)建時,需要自動繼承父線程的ThreadLocal變量,方便必要信息的進一步傳遞。

?著作權(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)容

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