深入理解ThreadLocal

2021-10-06 15:11:01 字數 3845 閱讀 5745

threadlocal例項為每乙個訪問它的執行緒(即當前執行緒)都關聯了乙個該執行緒的執行緒特有物件

執行緒特有物件(tso,thread specific object):各個執行緒建立各自的例項,乙個例項只能被乙個執行緒訪問的物件就被稱為執行緒特有物件,相對應的執行緒就被稱為該執行緒特有物件的持有執行緒

static  threadlocal

local =

newthreadlocal

()};

public

static

void

main

(string[

] args));

t[i]

.start();}}

執行結果:

首先,我們看一下threadlocal的所有方法:

其中紅色方框標註的是比較常用的方法。

咱們先看set(t)方法

public

void

set(t value)

通過thread.currentthread();獲取當前執行緒,然後通過當前執行緒去獲取當前執行緒的threadlocalmap ,也可以理解為threadlocalmap 就是執行緒的特有物件。

再看看threadlocalmap 是如何建立的?

void

createmap

(thread t, t firstvalue)

這個this就是threadlocal例項,再接著往下看

threadlocalmap

(threadlocal<

?> firstkey, object firstvalue)

static

class

entry

extends

weakreference

?>>

}

threadlocalmap有乙個entry物件陣列,而且這個entry是乙個(k,v)結構。通過threadlocal例項的hashcode值&(陣列容量值(initial_capacity) - 1)獲得下標。

我們畫個圖理解一下:

接下來,我們分析一下當threadlocalmap不為空的時候,set()方法是如何賦值的

private

void

set(threadlocal<

?> key, object value)

if(k == null)

} tab[i]

=new

entry

(key, value)

;int sz =

++size;if(

!cleansomeslots

(i, sz)

&& sz >= threshold)

rehash()

;}

通過threadlocal> key拿到entry 陣列的下標,然後通過for迴圈去線性探測,避免hash碰撞衝突

線性探測:屬於開放定址發。而hashmap的底層是使用了鏈定址法,即將相同hash值物件組成乙個鍊錶

通過下標獲得entry物件,然後迴圈當前下標之後的的entry直到entry為null。通過當前entry拿到k:

當k == key,直接賦值 e.value = value;

當k == null,因為entry 是不為空的,所以k為空,value是有值的。這說明threadlocal被**了,因為這裡k是弱引用。

接下來,我們來分析一下它是如何處理這些髒物件的

private

void

replacestaleentry

(threadlocal<

?> key, object value,

int staleslot)

if(k == null && slottoexpunge == staleslot)

slottoexpunge = i;

}// if key not found, put new entry in stale slot

tab[staleslot]

.value = null;

tab[staleslot]

=new

entry

(key, value)

;// if there are any other stale entries in run, expunge them

if(slottoexpunge != staleslot)

cleansomeslots

(expungestaleentry

(slottoexpunge)

, len)

;}

這裡有兩個迴圈,第乙個迴圈是以當前下標開始,向前迴圈,直到entry == null,找到最前的乙個髒物件(key==null&&value!=null)的下標,並將下標賦值給slottoexpunge

第二個迴圈是以當前下標開始,向後迴圈,也就是線性探測,直到entry == null,獲取當前entry的k:

當k == key時,替換value的值e.value = value,然後把髒物件替換當前entry物件

tab[i] = tab[staleslot],即當前物件成了髒物件。

這是為了防止當用乙個threadlocal進來放資料的時候放到了以前髒資料的位置,從而導致了同乙個threadlocal有兩個entry的問題。

接著判斷slottoexpunge 有沒有改變。如果沒有改變,則將當前下標賦值給slottoexpunge,最後將髒資料清理掉,即value=null,最後執行完畢,退出當前方法;

當k==null && slottoexpunge == staleslot,也就是當前entry前後的entry物件都是null,將當前下標賦值給slottoexpunge

最後,就剩下沒找到相同的key,則將當前entry清空,重新生成乙個entry,插入到當前下標下,同時清理其他髒物件

接下來,我們在來分析get()方法。

public t get()

}return

setinitialvalue()

;}

private t setinitialvalue()

get方法就簡單的多了,通過當前執行緒thread.currentthread(),得到該執行緒的threadlocalmap

如果threadlocalmap 不為空,通過threadlocal去拿取entry的value。

如果threadlocalmap 為空,則將初始話乙個threadlocal,並將threadlocal的初始值插入進去,最後返回。

private

void

remove

(threadlocal<

?> key)

}}

找到所有相同的threadlocal,然後將entry清

ThreadLocal深入理解

threadlocal從字面上理解,很容易會把threadlocal誤解為乙個執行緒的本地變數。threadlocal並不是代表當前執行緒,threadlocal其實是採用雜湊表的方式來為每個執行緒都提供乙個變數的副本。從而保證各個執行緒間資料安全。每個執行緒的資料不會被另外執行緒訪問和破壞。每個執...

ThreadLocal深入理解

通過每個執行緒維護一張threadlocalmap雜湊對映表,key為threadlocal的弱引用,value是object本身。也就是說,threadlocal本身不存任何實際值,而是通過本身作為key,從threadlocalmap中獲取具體的值。實際上,threadlocalmap儲存的是e...

深入理解threadlocal

threadlocal,很多地方叫做執行緒本地變數,也有些地方叫做執行緒本地儲存,其實意思差不多。可能很多朋友都知道threadlocal為變數在每個執行緒中都建立了乙個副本,那麼每個執行緒可以訪問自己內部的副本變數。這句話從字面上看起來很容易理解,但是真正理解並不是那麼容易。我們還是先來看乙個例子...