ThreadLocal父子線程之間的數(shù)據(jù)傳遞問題

一诺祸、問題的提出

在系統(tǒng)開發(fā)過程中常使用ThreadLocal進(jìn)行傳遞日志的RequestId携悯,由此來獲取整條請求鏈路。然而當(dāng)線程中開啟了其他的線程筷笨,此時(shí)ThreadLocal里面的數(shù)據(jù)將會(huì)出現(xiàn)無法獲茹竟怼/讀取錯(cuò)亂,甚至還可能會(huì)存在內(nèi)存泄漏等問題胃夏,下面用代碼來演示一下這個(gè)問題轴或。

普通代碼示例:

image

并行流代碼示例:

[圖片上傳中...(image-b7fe28-1545751320761-12)]

二、問題的解決

ThreadLocal的子類InheritableThreadLocal其實(shí)已經(jīng)幫我們處理好了仰禀,通過這個(gè)組件可以實(shí)現(xiàn)父子線程之間的數(shù)據(jù)傳遞照雁,在子線程中能夠父線程中的ThreadLocal本地變量。

三答恶、源碼的分析

image

可以看出InheritableThreadLocal繼承自ThreadLocal饺蚊,并重寫了三個(gè)相關(guān)方法。

再回來過來看ThreadLocal的源碼:

image

我們發(fā)現(xiàn)InheritableThreadLocal中createMap悬嗓,以及getMap方法處理的對象不一樣了污呼,其中在ThreadLocal中處理的是threadLocals,而InheritableThreadLocal中的是inheritableThreadLocals包竹,我們再順藤摸瓜看一下Thread對象的處理燕酷,其中在init源碼中我們看到這么一段代碼:

image

代碼的意思是在Thread獲取先父親線程parent(即要?jiǎng)?chuàng)建子線程的當(dāng)前這個(gè)線程)。當(dāng)父親線程中對inherThreadLocals進(jìn)行了賦值映企,就會(huì)把當(dāng)前線程的本地變量(也就是父線程的inherThreadLocals)進(jìn)行createInheritedMap方法操作悟狱。查看源碼createInheritedMap方法静浴,源碼可知此操作就是將賦線程的threadLocalMap傳遞給子線程堰氓。

image

我們寫個(gè)代碼測試一下:

image

看起來似乎真的是解決了我們無法傳遞的問題。

四苹享、真的就這么美好么双絮?我們來和線程池搭配一下

image

測試結(jié)果顯示兩次賦值,得到的結(jié)果還是第一次的值得问!為什么囤攀?

其實(shí)原因也很簡單,我們的線程池會(huì)緩存使用過的線程宫纬。當(dāng)線程需要被重復(fù)利用的時(shí)候焚挠,并不會(huì)再重新執(zhí)行init()初始化方法,而是直接使用已經(jīng)創(chuàng)建過的線程漓骚,所以這里的值不會(huì)二次產(chǎn)生變化蝌衔,那么該怎么做到真正的父子線程數(shù)據(jù)傳遞呢榛泛?

五、真正的解決方案:阿里的transmittable-thread-local了解一下

JDK的InheritableThreadLocal類可以完成父線程到子線程的值傳遞噩斟。但對于使用線程池等會(huì)池化復(fù)用線程的組件的情況曹锨,線程由線程池創(chuàng)建好,并且線程是池化起來反復(fù)使用的剃允;這時(shí)父子線程關(guān)系的ThreadLocal值傳遞已經(jīng)沒有意義沛简,應(yīng)用需要的實(shí)際上是把任務(wù)提交給線程池時(shí)的ThreadLocal值傳遞到任務(wù)執(zhí)行時(shí)。

首先分析一下最核心的類:TransmittableThreadLocal

image

首先TransmittableThreadLocal繼承自InheritableThreadLocal斥废,這樣可以在不破壞原有InheritableThreadLocal特性的情況下椒楣,還能充分使用Thread線程創(chuàng)建過程中執(zhí)行init方法,從而達(dá)到父子線程傳遞數(shù)據(jù)的目的牡肉。

這里有一個(gè)很重要的變量holder:源碼如下

1. holder中存放的是InheritableThreadLocal本地變量撒顿。

2. WeakHashMap支持存放空置。

image

主要的幾個(gè)相關(guān)方法:源碼如下

1. get方法調(diào)用時(shí)荚板,先獲取父親的相關(guān)數(shù)據(jù)判斷是否有數(shù)據(jù)凤壁,然后在holder中把自身也給加進(jìn)去。

2. set方法調(diào)用時(shí)跪另,先在父親中設(shè)置拧抖,再本地判斷是holder否為刪除或者是新增數(shù)據(jù)。

3. remove調(diào)用時(shí)免绿,先刪除自身唧席,再刪除父親中的數(shù)據(jù),刪除也是直接以自身this作為變量Key嘲驾。

image

采用包裝的形式來處理線程池中的線程不會(huì)執(zhí)行初始化的問題淌哟,源碼如下:

1. 先取得holder。

2. 備份線程本地?cái)?shù)據(jù)

3. run原先的方法

4. 還原線程本地?cái)?shù)據(jù)

image

備份方法:

1. 先獲取holder中的數(shù)據(jù)

2. 進(jìn)行迭代辽故,數(shù)據(jù)在captured中不存在徒仓,但是holder中存在,說明是后來加進(jìn)去的誊垢,進(jìn)行刪除掉弛。

3. 再將captured設(shè)置到當(dāng)前線程中。

image

還原方法:

1. 先獲取holder中的數(shù)據(jù)

2. backup中不存在喂走,holder中存在殃饿,說明是后面加進(jìn)去的,進(jìn)行刪除還原操作芋肠。

3. 再將backup設(shè)置到當(dāng)前線程中乎芳。

image

下面是幾個(gè)典型場景例子。

1. 分布式跟蹤系統(tǒng)

2. 日志收集記錄系統(tǒng)上下文

3. 應(yīng)用容器或上層框架跨應(yīng)用代碼給下層SDK傳遞信息

項(xiàng)目地址:https://github.com/alibaba/transmittable-thread-local

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奈惑,隨后出現(xiàn)的幾起案子谬晕,更是在濱河造成了極大的恐慌,老刑警劉巖携取,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攒钳,死亡現(xiàn)場離奇詭異,居然都是意外死亡雷滋,警方通過查閱死者的電腦和手機(jī)不撑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晤斩,“玉大人焕檬,你說我怎么就攤上這事“谋茫” “怎么了实愚?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長兔辅。 經(jīng)常有香客問我腊敲,道長,這世上最難降的妖魔是什么维苔? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任碰辅,我火速辦了婚禮,結(jié)果婚禮上介时,老公的妹妹穿的比我還像新娘没宾。我一直安慰自己,他們只是感情好沸柔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布循衰。 她就那樣靜靜地躺著,像睡著了一般褐澎。 火紅的嫁衣襯著肌膚如雪会钝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天乱凿,我揣著相機(jī)與錄音顽素,去河邊找鬼咽弦。 笑死徒蟆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的型型。 我是一名探鬼主播段审,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼闹蒜!你這毒婦竟也來了寺枉?” 一聲冷哼從身側(cè)響起抑淫,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姥闪,沒想到半個(gè)月后始苇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筐喳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年催式,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片避归。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荣月,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梳毙,到底是詐尸還是另有隱情哺窄,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布账锹,位于F島的核電站萌业,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奸柬。R本人自食惡果不足惜咽白,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸟缕。 院中可真熱鬧晶框,春花似錦、人聲如沸懂从。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽番甩。三九已至侵贵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缘薛,已是汗流浹背窍育。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宴胧,地道東北人漱抓。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像恕齐,于是被迫代替她去往敵國和親乞娄。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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

  • 一、簡介 并發(fā)編程中仪或,當(dāng)訪問共享數(shù)據(jù)時(shí)确镊,通常需要使用同步技術(shù)。但如果數(shù)據(jù)不發(fā)布(逸出)到線程以外范删,僅僅在單線程中被...
    邱simple閱讀 3,441評論 3 12
  • 本篇文章的主要內(nèi)容如下: 1蕾域、Java中的ThreadLocal2、 Android中的ThreadLocal3到旦、...
    Sophia_dd35閱讀 620評論 0 5
  • 本章寫了作者拉力加多束铭,到達(dá)了馬爾多納達(dá),由于沒有可以搭乘的船只厢绝,作者到格拉布杜德里布做了短暫的停留契沫,并受到了當(dāng)?shù)匦?..
    吉吉簡書閱讀 197評論 0 0
  • 梁祝歌詞 (女)碧草青青花盛開彩蝶雙雙久徘徊千古傳頌生生愛山伯永戀祝英臺 (男)同窗共讀整三載促膝并肩兩無猜十八相...
    樂度音樂閱讀 1,621評論 0 0
  • 小時(shí)候因?yàn)樨澩娓嬖V家長作業(yè)早就做完了,到學(xué)校又告訴老師昔汉,作業(yè)忘在家里了懈万。當(dāng)他們都相信你的時(shí)候,你心里得意忘形靶病,但是...
    遺落的一洛閱讀 219評論 0 2