一文帶你徹底搞懂ThreadLocal

2021-10-01 18:06:44 字數 3563 閱讀 8162

共享變數一直是併發中的老大難問題,每個執行緒都對它有操作權,所以執行緒之間的同步很關鍵,鎖也就應運而生。這裡換乙個思路,是否可以把共享變數私有化?即每個執行緒都擁有乙份共享變數的本地副本,每個執行緒對應乙個副本,同時對共享變數的操作也改為對屬於自己的副本的操作,這樣每個執行緒處理自己的本地變數,形成資料隔離。事實上這就是threadlocal了。

我們可能每天都在使用spring寫dao寫service,真的是乙個非常爽的框架.我們能用這麼爽是因為spring在底層把髒活兒累活兒全乾了。在乙個service中我們可能要寫很多個dao,如果多個dao都用不同的jdbc連線,很費時費力費資源不說,事務性就得不到保證了,因為我們知道事務需要在乙個連線內才能得以實現。事務對應著連線,所以如果我們每個執行緒對應乙個連線,也就能保證我們在乙個service中很爽的叨叨叨了,spring底層正是使用threadlocal對連線進行了封裝,可勁兒叨吧你就

看完應用場景似乎對threadlocal已經有所了解,不過,本著知其然並知其所以然的心態,還是去看看原始碼怎麼實現的吧

先去thread類中瞄一眼,thread中有2個成員變數

threadlocal.threadlocalmap threadlocals = null;

threadlocal.threadlocalmap inheritablethreadlocals = null;

兩個變數都是threadlocalmap類,咦?說了半天 threadlocal怎麼又冒出來個threadlocalmap

是這樣子的,預設這兩個變數都是null,正如我們現在看到的。

一旦我們呼叫threadlocal的set或者get才會真正建立他們。也就是你以為你把變數交給threadlocal了,其實這小子轉手就給threadlocalmap了,threadlocal就是套在 threadlocalmap外面的一層殼而已。

threadlocal的組成如下

可以看出,就跟map基本一樣,key是threadlocal的引用,value則是由開發者設定,即本地變數

set

public void set(t value) 

void createmap(thread t, t firstvalue)

首先獲取當前執行緒

以當前執行緒為key去查詢當前執行緒的map即threadlocals變數

如果threadlocals不為空,就把threadlocal引用作為key,value傳給map

否則建立map,也就是初始化當前執行緒的threadlocals變數

再次注意值存放的實際位置是thread中的threadlocalmap變數,threadlocalmap是乙個map,key是threadlocal的例項引用,值則是我們要存的變數

get
public t get() 

}return setinitialvalue();

}

其實就很簡單了,set的反過程嘛,先得到當前執行緒,然後得到成員變數threadlocals,如果threadlocals不為空,返回本地變數對應值,否則初始化threadlocals

初始化threadlocals

private t setinitialvalue() 

protected t initialvalue()

判斷當前threadlocals是否為空,如果不為空,設定當前threadlocal的例項引用對應變數為null,否則呼叫createmap建立threadlocals變數

remove()

public void remove()
如果threadlocals變數不為空,刪掉map中當前threadlocal對應例項引用的本地變數。

從上邊一路走下來我們應該了解了,每乙個執行緒中都有乙個threadlocalmap

型別的threadlocals變數,這個map中key為threadlocal的例項引用,value為對應的本地變數。如果這個執行緒不消亡,開發者也沒有採用remove操作及時清除掉不再使用的變數,這些變數就會一直存在map中,直到撐爆你的記憶體,造成記憶體溢位問題

假設乙個場景,我們需要非同步起乙個執行緒傳送郵件給使用者,子執行緒需要打日誌,那麼就需要父執行緒中本地變數,這裡子執行緒可以通過父執行緒的threadlocal得到父執行緒設定的的本地變數嗎?答案是不可以,threadlocal不支援父子執行緒間的繼承傳遞

這裡可以使用inheritablethreadlocal,沒錯就是上邊日誌那塊我們提到過的。它是threadlocal的子類,其實用法跟threadlocal完全一樣,不過,它可以使得子執行緒訪問在父執行緒中設定的變數,簡單看下這一實現的原理

首先看下執行緒的構造方法,說實話之前真沒看過這個構造方法,我覺得應該很多人都沒看過吧

public thread(runnable target)
這個init方法追進去,你會看到底下這行**,init方法體太長,我就只擷取出來這最關鍵的一部分

if (inheritthreadlocals && parent.inheritablethreadlocals != null)

this.inheritablethreadlocals =

threadlocal.createinheritedmap(parent.inheritablethreadlocals);

parent就是父執行緒,如果父執行緒中的inheritablethreadlocals 不為空,就執行 createinheritedmap

static threadlocalmap createinheritedmap(threadlocalmap parentmap)
這個**就不繼續展開了,過程就是把父執行緒中的 inheritablethreadlocals 的值全部複製乙份到子執行緒的 inheritablethreadlocals 中

每個執行緒對應乙個threadlocalmap,threadlocal其實就是套在threadlocalmap上的一層殼

threadlocalmap的key是threadlocal的例項引用,value是我們像設定的本地變數

若是執行緒一直不停,threadlocalmap中的本地變數就會越來越多,注意即使remove掉不再使用的變數,防止記憶體溢位

一文帶你搞懂Spring核心

容錯 減少延遲 提高效能 可用性負載均衡 總而言之,其實目的只有乙個,使用者體驗 分布式系統是由使用分發中介軟體連線的自治計算機組成的網路。它們有助於共享不同的資源和功能,為使用者提供單一且整合的連貫網路。1 在伺服器 虛擬機器 上安裝zookeeper 我這裡使用docker安裝 1 去docke...

一文帶你搞懂python中的property

通常我們在獲得變數的一些私有屬性時,必須通過方法來獲取私有屬性,並不能直接訪問 修改其數值的時候也是要通過方法去修改,這樣非常的不方便 所以python提供了一種方式,將呼叫方法的的形式轉變為訪問屬性,這樣使用非常方便 class student def init self 這是乙個私有屬性 sel...

Go學習 一文徹底搞懂go mod使用

jquery中文網為您提供一文徹底搞懂go mod使用等資源,歡迎您收藏本站,我們將為您提供最新的一文徹底搞懂go mod使用資源 目錄 一 如何使用go mod 二 golang開啟mod後import報紅解決 三 go mod 怎麼匯入本地其它專案的包?四 完整 此時專案可以放在任意位置,不必非...