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為變數在每個執行緒中都建立了乙個副本,那麼每個執行緒可以訪問自己內部的副本變數。這句話從字面上看起來很容易理解,但是真正理解並不是那麼容易。我們還是先來看乙個例子...