最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java開發(fā)工作中對InheritableThreadLocal使用思考

 更新時間:2022年11月15日 11:47:43   作者:方圓想當圖靈  
這篇文章主要為大家介紹了java開發(fā)工作中對InheritableThreadLocal使用思考詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

最近在工作中結合線程池使用 InheritableThreadLocal 出現了獲取線程變量“錯誤”的問題,看了相關的文檔和源碼后在此記錄。

1. 先說結論

InheritableThreadLocal 只有在父線程創(chuàng)建子線程時,在子線程中才能獲取到父線程中的線程變量;

當配合線程池使用時:“第一次在線程池中開啟線程,能在子線程中獲取到父線程的線程變量,而當該子線程開啟之后,發(fā)生線程復用,該子線程仍然保留的是之前開啟它的父線程的線程變量,而無法獲取當前父線程中新的線程變量”,所以會發(fā)生獲取線程變量錯誤的情況。

2. 實驗例子

  • 創(chuàng)建一個線程數固定為1的線程池,先在main線程中存入變量1,并使用線程池開啟新的線程打印輸出線程變量,之后更改main線程的線程變量為變量2,再使用線程池中線程(發(fā)生線程復用)打印輸出線程變量,對比兩次輸出的值是否不同
/**
 * 測試線程池下InheritableThreadLocal線程變量失效的場景
 */
public class TestInheritableThreadLocal {
    private static final InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    // 固定大小的線程池,保證線程復用
    private static final ExecutorService executorService = Executors.newFixedThreadPool(1);
    public static void main(String[] args) {
        threadLocal.set("main線程 變量1");
        // 正常取到 main線程 變量1
        executorService.execute(() -> System.out.println(threadLocal.get()));
        threadLocal.set("main線程 變量2");
        // 線程復用再取還是 main線程 變量1
        executorService.execute(() -> System.out.println(threadLocal.get()));
    }
}

輸出結果:

main線程 變量1 main線程 變量1

發(fā)現兩次輸出結果值相同,證明發(fā)生線程復用時,子線程獲取父線程變量失效

3. 詳解

3.1 JavaDoc

This class extends ThreadLocal to provide inheritance of values from parent thread to child thread: when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values. Normally the child's values will be identical to the parent's; however, the child's value can be made an arbitrary function of the parent's by overriding the childValue method in this class. 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.

InheritableThreadLocal 繼承了 ThreadLocal, 以能夠讓子線程能夠從父線程中繼承線程變量: 當一個子線程被創(chuàng)建時,它會接收到父線程中所有可繼承的變量。通常情況下,子線程和父線程中的線程變量是完全相同的,但是可以通過重寫 childValue 方法來使父子線程中的值不同。

當線程中維護的變量如UserId, TransactionId 等必須自動傳遞到新創(chuàng)建的任何子線程時,使用InheritableThreadLocal要優(yōu)于ThreadLocal

3.2 源碼

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * 當子線程被創(chuàng)建時,通過該方法來初始化子線程中線程變量的值,
     * 這個方法在父線程中被調用,并且在子線程開啟之前。
     * 
     * 通過重寫這個方法可以改變從父線程中繼承過來的值。
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    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);
    }
}

其中childValue方法來獲取父線程中的線程變量的值,也可通過重寫這個方法來將獲取到的線程變量的值進行修改。

getMap方法和createMap方法中,可以發(fā)現inheritableThreadLocals變量,它是 ThreadLocalMap,在Thread類

3.2.1 childValue方法

  • 開啟新線程時,會調用Thread的構造方法
    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }
  • 沿著構造方法向下,找到init方法的最終實現,其中有如下邏輯:為當前線程創(chuàng)建線程變量以繼承父線程中的線程變量
/**
 * @param inheritThreadLocals 為ture,代表是為 包含可繼承的線程變量 的線程進行初始化
 */
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    ...
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        // 注意這里創(chuàng)建子線程的線程變量
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    ...
}

ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)創(chuàng)建子線程 InheritedMap 的具體實現

createInheritedMap 方法,最終會調用到 ThreadLocalMap私有構造方法,傳入的參數parentMap即為父線程中保存的線程變量

    private ThreadLocalMap(ThreadLocalMap parentMap) {
        Entry[] parentTable = parentMap.table;
        int len = parentTable.length;
        setThreshold(len);
        table = new Entry[len];
        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方法
                    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++;
                }
            }
        }
    }

這個方法會對父線程中的線程變量做深拷貝,其中調用了childValue方法來獲取/初始化子線程中的值,并保存到子線程中

  • 由上可見,可繼承的線程變量只是在線程被創(chuàng)建的時候進行了初始化工作,這也就能解釋為什么在線程池中發(fā)生線程復用時不能獲取到父線程線程變量的原因

4. 實驗例子流程圖

  • main線程set main線程 變量1時,會調用到InheritableThreadLocalcreateMap方法,創(chuàng)建 inheritableThreadLocals 并保存線程變量
  • 開啟子線程1時,會深拷貝父線程中的線程變量到子線程中,如圖示
  • main線程set main線程 變量2,會覆蓋主線程中之前set的mian線程變量1
  • 最后發(fā)生線程復用,子線程1無法獲取到main線程新set的值,仍然打印 main線程 變量1

以上就是java開發(fā)工作中對InheritableThreadLocal使用思考的詳細內容,更多關于java開發(fā)InheritableThreadLocal的資料請關注腳本之家其它相關文章!

相關文章

最新評論

荔浦县| 梅州市| 顺义区| 扶风县| 平谷区| 盐山县| 泾川县| 太原市| 博乐市| 丰都县| 化隆| 长海县| 思茅市| 冀州市| 邯郸市| 丹凤县| 华池县| 五大连池市| 麻江县| 钟祥市| 徐水县| 兴安县| 绥中县| 饶平县| 庄河市| 海伦市| 眉山市| 尤溪县| 将乐县| 镇赉县| 宽甸| 红安县| 余庆县| 仁布县| 白沙| 南阳市| 团风县| 四平市| 南郑县| 化隆| 新兴县|