0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ThreadLocal源碼解析及實(shí)戰(zhàn)應(yīng)用

OSC開源社區(qū) ? 來源:OSCHINA 社區(qū) ? 2023-01-29 14:53 ? 次閱讀

來源| OSCHINA 社區(qū)

作者 | 京東云開發(fā)者-京東物流 閆鵬勃

1 什么是 ThreadLocal?

ThreadLocal 是一個(gè)關(guān)于創(chuàng)建線程局部變量的類。

通常情況下,我們創(chuàng)建的變量是可以被任何一個(gè)線程訪問并修改的。而使用 ThreadLocal 創(chuàng)建的變量只能被當(dāng)前線程訪問,其他線程則無法訪問和修改。ThreadLocal 在設(shè)計(jì)之初就是為解決并發(fā)問題而提供一種方案,每個(gè)線程維護(hù)一份自己的數(shù)據(jù),達(dá)到線程隔離的效果。

2 有什么作用?

2.1 set once,get everywhere

在現(xiàn)在的系統(tǒng)設(shè)計(jì)中,前后端分離已基本成為常態(tài),分離之后如何獲取用戶信息就成了一件麻煩事,通常在用戶登錄后, 用戶信息會(huì)保存在 Session 或者 Token 中。這個(gè)時(shí)候,我們?nèi)绻褂贸R?guī)的手段去獲取用戶信息會(huì)很費(fèi)勁,拿 Session 來說,我們要在接口參數(shù)中加上 HttpServletRequest 對(duì)象,然后調(diào)用 getSession 方法,且每一個(gè)需要用戶信息的接口都要加上這個(gè)參數(shù),才能獲取 Session,這樣實(shí)現(xiàn)就很麻煩了。 在實(shí)際的系統(tǒng)設(shè)計(jì)中,我們肯定不會(huì)采用上面所說的這種方式,而是使用 ThreadLocal,我們會(huì)選擇在攔截器的業(yè)務(wù)中, 獲取到保存的用戶信息,然后存入 ThreadLocal,那么當(dāng)前線程在任何地方如果需要拿到用戶信息都可以使用 ThreadLocal 的 get () 方法 (異步程序中 ThreadLocal 是不可靠的)

2.2 線程安全,空間換時(shí)間

在 Spring 的 Web 項(xiàng)目中,我們通常會(huì)將業(yè)務(wù)分為 Controller 層,Service 層,Dao 層, 我們都知道@Autowired 注解默認(rèn)使用單例模式,那么不同請(qǐng)求線程進(jìn)來之后,由于 Dao 層使用單例,那么負(fù)責(zé)數(shù)據(jù)庫連接的 Connection 也只有一個(gè), 如果每個(gè)請(qǐng)求線程都去連接數(shù)據(jù)庫,那么就會(huì)造成線程不安全的問題,Spring 是如何解決這個(gè)問題的呢? 在 Spring 項(xiàng)目中 Dao 層中裝配的 Connection 肯定是線程安全的,其解決方案就是采用 ThreadLocal 方法,當(dāng)每個(gè)請(qǐng)求線程使用 Connection 的時(shí)候, 都會(huì)從 ThreadLocal 獲取一次,如果為 null,說明沒有進(jìn)行過數(shù)據(jù)庫連接,連接后存入 ThreadLocal 中,如此一來,每一個(gè)請(qǐng)求線程都保存有一份 自己的 Connection。于是便解決了線程安全問題

3 ThreadLocal 實(shí)戰(zhàn)應(yīng)用

3.1 ehr 中的使用

在登錄攔截器中將用戶信息寫入,后續(xù)使用時(shí)方便取值

3e989f48-9693-11ed-bfe3-dac502259ad0.png3eb826a6-9693-11ed-bfe3-dac502259ad0.png

3.2 分頁插件 PageHelper 中的應(yīng)用

3eddc1a4-9693-11ed-bfe3-dac502259ad0.png3ef7e3cc-9693-11ed-bfe3-dac502259ad0.png

3.3 AopContext

3f0e2646-9693-11ed-bfe3-dac502259ad0.png

4 源碼解讀

你是否有這樣的疑惑?為什么可以直接拿到?對(duì)象存放在哪里?存在什么問題?

4.1 get 方法

在 get () 方法中也會(huì)獲取到當(dāng)前線程的 ThreadLocalMap,如果 ThreadLocalMap 不為 null,則把獲取 key 為當(dāng)前 ThreadLocal 的值;否則調(diào)用 setInitialValue () 方法返回初始值,并保存到新創(chuàng)建的 ThreadLocalMap 中。 3f28a94e-9693-11ed-bfe3-dac502259ad0.png

4.2 set 方法

調(diào)用 set 時(shí),直接調(diào)用 set (T value) 方法中,首先獲取當(dāng)前線程,然后在獲取到當(dāng)前線程的 ThreadLocalMap,如果 ThreadLocalMap 不為 null,則將 value 保存到 ThreadLocalMap 中,并用當(dāng)前 ThreadLocal 作為 key;否則創(chuàng)建一個(gè) ThreadLocalMap 并給到當(dāng)前線程,然后保存 value。 ThreadLocalMap 相當(dāng)于一個(gè) HashMap,是真正保存值的地方
map 的 set,如果 map 為空,則創(chuàng)建一個(gè)

3f4305f0-9693-11ed-bfe3-dac502259ad0.png3f5e517a-9693-11ed-bfe3-dac502259ad0.png

4.3 initialValue () 方法

initialValue () 是 ThreadLocal 的初始值,默認(rèn)返回 null,子類可以重寫改方法,用于設(shè)置 ThreadLocal 的初始值。 3f7e11a4-9693-11ed-bfe3-dac502259ad0.png

4.4 remove () 方法

ThreadLocal 還有一個(gè) remove () 方法,用來移除當(dāng)前 ThreadLocal 對(duì)應(yīng)的值。同樣也是同過當(dāng)前線程的 ThreadLocalMap 來移除相應(yīng)的值。 3f9ef81a-9693-11ed-bfe3-dac502259ad0.png

getMap 拿到了什么?


在 set,get,initialValue 和 remove 方法中都會(huì)獲取到當(dāng)前線程,然后通過當(dāng)前線程獲取到 ThreadLocalMap,如果 ThreadLocalMap 為 null,則會(huì)創(chuàng)建一個(gè) ThreadLocalMap,并給到當(dāng)前線程 3fba01a0-9693-11ed-bfe3-dac502259ad0.png

此處 t 是 Thread,直接可以 “點(diǎn)” 拿到這個(gè) map
每個(gè) Thread 對(duì)象內(nèi)部都維護(hù)了一個(gè) ThreadLocalMap 這樣一個(gè) ThreadLocal 的 Map,可以存放若干個(gè) ThreadLocal
3fcc3a32-9693-11ed-bfe3-dac502259ad0.png

在使用 ThreadLocal 類型變量進(jìn)行相關(guān)操作時(shí),都會(huì)通過當(dāng)前線程獲取到 ThreadLocalMap 來完成操作。每個(gè)線程的 ThreadLocalMap 是屬于線程自己的,ThreadLocalMap 中維護(hù)的值也是屬于線程自己的。這就保證了 ThreadLocal 類型的變量在每個(gè)線程中是獨(dú)立的,在多線程環(huán)境下不會(huì)相互影響。

5 使用注意事項(xiàng)

1)有可能導(dǎo)致內(nèi)存泄漏,使用完畢后,需要 remove 在 ThreadLocalMap 的 set (),get () 和 remove () 方法中,都有清除無效 Entry 的操作,這樣做是為了降低內(nèi)存泄漏發(fā)生的可能。


Entry 中的 key 使用了弱引用的方式,這樣做是為了降低內(nèi)存泄漏發(fā)生的概率,但不能完全避免內(nèi)存泄漏。 3ffbe872-9693-11ed-bfe3-dac502259ad0.png


假設(shè) Entry 的 key 沒有使用弱引用的方式,而是使用了強(qiáng)引用:由于 ThreadLocalMap 的生命周期和當(dāng)前線程一樣長,那么當(dāng)引用 ThreadLocal 的對(duì)象被回收后,由于 ThreadLocalMap 還持有 ThreadLocal 和對(duì)應(yīng) value 的強(qiáng)引用,ThreadLocal 和對(duì)應(yīng)的 value 是不會(huì)被回收的,這就導(dǎo)致了內(nèi)存泄漏。所以 Entry 以弱引用的方式避免了 ThreadLocal 沒有被回收而導(dǎo)致的內(nèi)存泄漏,但是此時(shí) value 仍然是無法回收的,依然會(huì)導(dǎo)致內(nèi)存泄漏。

ThreadLocalMap 已經(jīng)考慮到這種情況,并且有一些防護(hù)措施:在調(diào)用 ThreadLocal 的 get (),set () 和 remove () 的時(shí)候都會(huì)清除當(dāng)前線程 ThreadLocalMap 中所有 key 為 null 的 value。這樣可以降低內(nèi)存泄漏發(fā)生的概率。所以我們?cè)谑褂?ThreadLocal 的時(shí)候,每次用完 ThreadLocal 都調(diào)用 remove () 方法,清除數(shù)據(jù),防止內(nèi)存泄漏。


2)使用線程池時(shí),父子線程傳遞慎用,因?yàn)槌跏蓟瘯r(shí)機(jī)為線程創(chuàng)建時(shí)

402e53b6-9693-11ed-bfe3-dac502259ad0.png

3)針對(duì) 2 有什么方案可以解決?
TransmittableThreadLocal
源碼地址:https://github.com/alibaba/transmittable-thread-local

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)庫
    +關(guān)注

    關(guān)注

    7

    文章

    3712

    瀏覽量

    64025
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    626

    瀏覽量

    28969
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14259
  • 變量
    +關(guān)注

    關(guān)注

    0

    文章

    607

    瀏覽量

    28257
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    501

    瀏覽量

    19580

原文標(biāo)題:ThreadLocal 源碼解析及實(shí)戰(zhàn)應(yīng)用

文章出處:【微信號(hào):OSC開源社區(qū),微信公眾號(hào):OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    ThreadLocal實(shí)例應(yīng)用

    ThreadLocal相信大家都用過,但你知道他的原理嗎,今天了不起帶大家學(xué)習(xí)ThreadLocal。 ThreadLocal是什么 在多線程編程中,經(jīng)常會(huì)遇到需要在不同線程中共享數(shù)據(jù)的情況
    的頭像 發(fā)表于 09-30 10:19 ?587次閱讀
    <b class='flag-5'>ThreadLocal</b>實(shí)例應(yīng)用

    ThreadLocal的定義、用法及優(yōu)點(diǎn)

    ThreadLocal 簡介 ThreadLocal是Java中一個(gè)非常重要的線程技術(shù)。它可以讓每個(gè)線程都擁有自己的變量副本,避免了線程間的競爭和數(shù)據(jù)泄露問題。在本文中,我們將詳細(xì)介紹
    的頭像 發(fā)表于 09-30 10:14 ?879次閱讀
    <b class='flag-5'>ThreadLocal</b>的定義、用法及優(yōu)點(diǎn)

    C語言實(shí)戰(zhàn)105例源碼

    C語言實(shí)戰(zhàn)105例源碼
    發(fā)表于 08-20 12:40

    Spark運(yùn)行架構(gòu)與源碼解析

    Spark 源碼解析DAGScheduler中的DAG劃分與提交
    發(fā)表于 04-24 06:32

    用在解析云端數(shù)據(jù)的源碼是怎樣的

    用在解析云端數(shù)據(jù)的源碼是怎樣的?如何去實(shí)現(xiàn)這種源碼呢?
    發(fā)表于 10-18 09:00

    uCOS3源碼解析教程

    uCOS3源碼解析視頻教程-第4季第7部分 互聯(lián)網(wǎng)課程品牌《朱老師物聯(lián)網(wǎng)大講...
    發(fā)表于 01-12 07:46

    對(duì)FreeRTOS的實(shí)戰(zhàn)學(xué)習(xí)以及源碼分析

    整個(gè)專欄主要是博主結(jié)合自身對(duì)FreeRTOS的實(shí)戰(zhàn)學(xué)習(xí)以及源碼分析,基于STM32F767 Nucleo-144平臺(tái),在CubeIDE下進(jìn)行開發(fā),結(jié)合官方的HAL庫,將硬件環(huán)節(jié)的問題減少到最小,將精力主要放在RTOS的學(xué)習(xí)上
    發(fā)表于 02-11 07:18

    Uboot中start.S源碼的指令級(jí)的詳盡解析

    Uboot中start.S源碼的指令級(jí)的詳盡解析
    發(fā)表于 10-30 08:47 ?28次下載
    Uboot中start.S<b class='flag-5'>源碼</b>的指令級(jí)的詳盡<b class='flag-5'>解析</b>

    ThreadLocal發(fā)生內(nèi)存泄漏的原因

    前言 ThreadLocal 的作用是提供線程內(nèi)的局部變量,這種變量在線程的生命周期內(nèi)起作用,減少同一個(gè)線程內(nèi)多個(gè)函數(shù)或者組件之間一些公共變量的傳遞的復(fù)雜度。但是如果濫用 ThreadLocal
    的頭像 發(fā)表于 05-05 16:23 ?3605次閱讀

    Navigation源碼解析

    Navigation源碼解析 谷歌推出Navigation主要是為了統(tǒng)一應(yīng)用內(nèi)頁面跳轉(zhuǎn)行為。本文主要是根據(jù)Navigation版本為2.1.0 的源碼進(jìn)行講解
    的頭像 發(fā)表于 06-15 16:38 ?1667次閱讀

    如何使用ThreadLocal來避免內(nèi)存泄漏

    本次給大家介紹重要的工具ThreadLocal。講解內(nèi)容如下,同時(shí)介紹什么場景下發(fā)生內(nèi)存泄漏,如何復(fù)現(xiàn)內(nèi)存泄漏,如何正確使用它來避免內(nèi)存泄漏。 ThreadLocal是什么?有哪些用途
    的頭像 發(fā)表于 08-20 09:29 ?4109次閱讀
    如何使用<b class='flag-5'>ThreadLocal</b>來避免內(nèi)存泄漏

    簡述hex文件解析源碼

    簡述hex文件解析源碼
    發(fā)表于 09-12 09:20 ?8次下載

    云海計(jì)費(fèi)系統(tǒng)v4.1 視頻解析解析收費(fèi)接口專用 短視頻解析解析收費(fèi)接口專用 影視視頻電影解析計(jì)費(fèi)平臺(tái)源碼程序

    介紹:云海計(jì)費(fèi)系統(tǒng)v4.1 視頻解析 短視頻解析 影視視頻電影解析計(jì)費(fèi)平臺(tái)源碼程序云海解析計(jì)費(fèi)系統(tǒng)是一款VIP視頻計(jì)費(fèi)
    發(fā)表于 01-11 16:02 ?13次下載
    云海計(jì)費(fèi)系統(tǒng)v4.1 視頻<b class='flag-5'>解析</b><b class='flag-5'>解析</b>收費(fèi)接口專用 短視頻<b class='flag-5'>解析</b><b class='flag-5'>解析</b>收費(fèi)接口專用 影視視頻電影<b class='flag-5'>解析</b>計(jì)費(fèi)平臺(tái)<b class='flag-5'>源碼</b>程序

    node.js實(shí)戰(zhàn)源碼

    node.js實(shí)戰(zhàn)源碼
    發(fā)表于 05-16 18:06 ?1次下載

    ThreadLocal基本內(nèi)容與用法

    下面我們就來看看道哥都用的ThreadLocal。 1 ThreadLocal你來自哪里 Since : 1.2 Author : Josh Bloch and Doug Lea 又是并發(fā)大佬們
    的頭像 發(fā)表于 10-13 11:39 ?388次閱讀