InheritableThreadLocal變量無法傳遞

背景知識

我們都知道InheritableThreadLocal類型的變量可以在父子線程之間傳遞煎娇。具體實現(xiàn)為:每個線程都維護了threadLocalsinheritableThreadLocals成員變量梧却,線程在初始化時會根據(jù)父線程的inheritableThreadLocals創(chuàng)建自身的inheritableThreadLocals竣贪。具體的創(chuàng)建由ThreadLocal類負責實現(xiàn)。

線程的threadLocalsinheritableThreadLocals成員變量都是ThreadLocal.ThreadLocalMap類型柒瓣,這個類在功能上類似于HashMap喳魏,是ThreadLocal或者InheritableThreadLocal類型變量的集合卖毁。
Thread.java

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

init是線程的初始化方法,注意這里省略了無關(guān)的一些代碼
Thread.java

/**
 * Initializes a Thread.
 *
 * @param g the Thread group
 * @param target the object whose run() method gets called
 * @param name the name of the new Thread
 * @param stackSize the desired stack size for the new thread, or
 *        zero to indicate that this parameter is to be ignored.
 * @param acc the AccessControlContext to inherit, or
 *            AccessController.getContext() if null
 * @param inheritThreadLocals if {@code true}, inherit initial values for
 *            inheritable thread-locals from the constructing thread
 */
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    ……
    Thread parent = currentThread();
    ……
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    ……
}

問題描述

筆者最近在使用InheritableThreadLocal時無意中碰到了一個問題贩毕。先來看我自定義的ThreadLocal類型悯许,這個類的默認初始值為當前時間戳。

MyThreadLocal.java

import java.util.Date;

public class MyThreadLocal extends InheritableThreadLocal<Long> {

    @Override
    protected Long initialValue() {
        return new Date().getTime();
    }
}

在這里我創(chuàng)建了一個子線程myThread辉阶,然后打印當前線程的ThreadLocal變量的值先壕,過2秒后啟動子線程,由子線程打印這個ThreadLocal變量的值谆甜。
MyThread.java

public class MyThread extends Thread {

    public static ThreadLocal<Long> tl = new MyThreadLocal();

    @Override
    public void run() {
        System.out.println(tl.get());
    }

    public static void main(String args[]) {
        MyThread myThread = new MyThread();
        System.out.println(tl.get());

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        myThread.start();
    }
}

由于MyThreadLocal繼承了InheritableThreadLocal垃僚,因此我期望兩者可以打印出相同的結(jié)果。然而無論運行多少遍這個程序规辱,實際結(jié)果總是相差2秒以上(非常接近2秒)谆棺。由此可見這個ThreadLocal變量并沒有從父線程傳遞到子線程。

問題分析

筆者試著通過分析源代碼來查找原因罕袋「氖纾可以看到線程中的threadLocals變量默認為null,而且Thread類中并沒有任何對其賦值的地方浴讯,事實上通過注釋也可以發(fā)現(xiàn)threadLocalsinheritableThreadLocals都是由ThreadLocal類維護的

ThreadLocal類的createMap方法為線程初始化threadLocals變量
ThreadLocal.java

/**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

InheritableThreadLocal類的createMap方法為線程初始化inheritableThreadLocals變量
InheritableThreadLocal.java

/**
 * Create the map associated with a ThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the table.
 */
void createMap(Thread t, T firstValue) {
    t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}

而調(diào)用createMap方法的地方為ThreadLocal的set方法和setInitialValue方法朵夏,后者被get方法調(diào)用。ThreadLocal本身的構(gòu)造函數(shù)并沒有做任何事情榆纽,即使我們通過重寫initialValue方法設(shè)置默認值侍郭。也就是說询吴,如果我們創(chuàng)建了ThreadLocal類型的對象后沒有調(diào)用set方法設(shè)置初始值或者調(diào)用get方法設(shè)置默認的初始值,那么這個對象不會被添加到線程的threadLocals或者inheritableThreadLocals中亮元。此時創(chuàng)建的子線程也不能獲取到父線程的InheritableThreadLocal類型的變量。由此導致了InheritableThreadLocal變量無法在父子線程之間傳遞唠摹。
當然解決方法也很簡單爆捞,在子線程創(chuàng)建之前調(diào)用set方法設(shè)置變量的值或者調(diào)用get方法獲取默認值即可。

結(jié)論

InheritableThreadLocal類型的變量要在子線程創(chuàng)建之前設(shè)置初始值勾拉,否則不能在父子線程之間共享煮甥。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市藕赞,隨后出現(xiàn)的幾起案子成肘,更是在濱河造成了極大的恐慌,老刑警劉巖斧蜕,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件双霍,死亡現(xiàn)場離奇詭異,居然都是意外死亡批销,警方通過查閱死者的電腦和手機洒闸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來均芽,“玉大人丘逸,你說我怎么就攤上這事∠扑危” “怎么了深纲?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長劲妙。 經(jīng)常有香客問我湃鹊,道長,這世上最難降的妖魔是什么是趴? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任涛舍,我火速辦了婚禮,結(jié)果婚禮上唆途,老公的妹妹穿的比我還像新娘富雅。我一直安慰自己,他們只是感情好肛搬,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布没佑。 她就那樣靜靜地躺著,像睡著了一般温赔。 火紅的嫁衣襯著肌膚如雪蛤奢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音啤贩,去河邊找鬼待秃。 笑死,一個胖子當著我的面吹牛痹屹,可吹牛的內(nèi)容都是我干的章郁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼志衍,長吁一口氣:“原來是場噩夢啊……” “哼暖庄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起楼肪,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤培廓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后春叫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肩钠,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年象缀,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔬将。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡央星,死狀恐怖霞怀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莉给,我是刑警寧澤毙石,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站颓遏,受9級特大地震影響徐矩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叁幢,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一滤灯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧曼玩,春花似錦鳞骤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至顷帖,卻和暖如春美旧,著一層夾襖步出監(jiān)牢的瞬間渤滞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工榴嗅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妄呕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓录肯,卻偏偏與公主長得像趴腋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子论咏,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 本篇文章的主要內(nèi)容如下: 1、Java中的ThreadLocal2颁井、 Android中的ThreadLocal3厅贪、...
    Sophia_dd35閱讀 620評論 0 5
  • Java SE 基礎(chǔ): 封裝、繼承雅宾、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個獨立的整體养涮,并盡...
    Jayden_Cao閱讀 2,109評論 0 8
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,104評論 0 62
  • 人踩著落葉回家眉抬,覺著這樣會過一輩子贯吓。 每天一百字||今天繼續(xù)說胸脅痛,順便再發(fā)個廣告蜀变。如果氣滯胸脅就會胸脅痛悄谐,在腸...
    張安默生閱讀 186評論 0 0
  • 我關(guān)閉了微信朋友圈的發(fā)現(xiàn)功能。今天開始库北,我大概也不會再更新自己的朋友圈爬舰。若不是因為工作,仍需要用到微信寒瓦,我想連微信...
    俗然閱讀 959評論 4 26