一.threadlocal概述
顧名思義線(xiàn)程本地存儲(chǔ)阀蒂,如果定義了一個(gè)threadlocal對(duì)象该窗,每個(gè)線(xiàn)程往這個(gè)threadlocal對(duì)象中的讀寫(xiě)是隔離的,可以為相同對(duì)象在不同線(xiàn)程中都創(chuàng)建不同的存儲(chǔ)蚤霞,因此如果多個(gè)線(xiàn)程都使用變量x酗失,那線(xiàn)程本地存儲(chǔ)會(huì)生成多個(gè)用于x的不同存儲(chǔ)塊。
二.threadlocal實(shí)現(xiàn)的大致思路
Thread類(lèi)有一個(gè)ThreadLocal.threadLocalMap類(lèi)型的實(shí)例變量threadlocals争便,也就是說(shuō)每個(gè)線(xiàn)程都有一個(gè)threadLocalMap级零,threadLocalMap為定義在threadlocal內(nèi)的靜態(tài)內(nèi)部類(lèi),雖是一個(gè)map但沒(méi)有實(shí)現(xiàn)map接口滞乙,有自己獨(dú)立的實(shí)現(xiàn)奏纪。threadLocalMap的是使用開(kāi)放尋址線(xiàn)性探測(cè)的哈希表,節(jié)點(diǎn)Entry繼承自弱引用WeakReference斩启,本身為指向threadlocal的弱引用序调,有一個(gè)value屬性用來(lái)存放放到threadlocal內(nèi)的值⊥么兀可以簡(jiǎn)單的認(rèn)為key為threadlocal對(duì)象发绢,值為放入threadlocal的具體值。每個(gè)線(xiàn)程往threadlocal中塞值的時(shí)候垄琐,都會(huì)以該threadlocal對(duì)象為key边酒,往自己的threadlocalmap中存,讀也是以某個(gè)threadlocal為引用狸窘,在自己的threadlocalmap中找對(duì)應(yīng)的key墩朦,從而實(shí)現(xiàn)線(xiàn)程隔離。
圖為T(mén)hread類(lèi)中threadlocalmap實(shí)例變量:
ThreadLocal.ThreadLocalMap threadLocals = null;
三.API
threadLocalMap的api:
四.threadlocal API
get方法:
public T get() {
//獲取當(dāng)前線(xiàn)程的threadlocalmap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//如果線(xiàn)程中的threadlocalmap非空翻擒,則以這個(gè)threadlocal為key查詢(xún)對(duì)應(yīng)的value
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//否則設(shè)置初始化值
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
//可以客戶(hù)端自己繼承threadlocal重寫(xiě)該類(lèi)氓涣,否則如果沒(méi)有set就get的話(huà)會(huì)拋出空指針錯(cuò)誤
protected T initialValue() {
return null;
}
set,remove:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
可以看出get set remove方法都是獲得當(dāng)前線(xiàn)程的threadlocalmap,然后調(diào)用他的get set remove方法陋气。
五.ThreadLocalMap詳解
entry:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
entry本身為一個(gè)弱引用劳吠,定義了一個(gè)類(lèi)型為object的value
1.1為什么使用弱引用:
如果使用普通的key-value形式存儲(chǔ),那么就造成threadlocal對(duì)象的生命周期與線(xiàn)程一樣長(zhǎng)巩趁,因?yàn)榭倳?huì)存在thread->threadlocalmap->entry->threadlocal這樣一條引用鏈存在痒玩,只要線(xiàn)程沒(méi)被銷(xiāo)毀,只要線(xiàn)程沒(méi)有被銷(xiāo)毀议慰,那么節(jié)點(diǎn)在gc可達(dá)性分析中一直處于可達(dá)狀態(tài)凰荚,無(wú)法被gc回收。而使用弱引用褒脯,如果threadlocal沒(méi)有強(qiáng)引用可達(dá)便瑟,那么它不會(huì)活過(guò)下次gc,而該threadlocal對(duì)應(yīng)的entry會(huì)失效番川,這位threadlocalmap垃圾清理提供了便利到涂。
1.2類(lèi)的成員變量和相應(yīng)方法:
/**
* 初始容量16脊框,容量必須為2的冪
*/
private static final int INITIAL_CAPACITY = 16;
/**
* 內(nèi)部其實(shí)是entry類(lèi)型的數(shù)組
*/
private Entry[] table;
/**
* 表中entry的個(gè)數(shù)
*/
private int size = 0;
/**
* 裝載因子
*/
private int threshold; // Default to 0
/**
* 最差的裝載因子為2/3
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
threadlocalmap為使用線(xiàn)性探測(cè)的哈希表,nextIndex和prevIndex為獲取表前一個(gè)元素和后一個(gè)元素的方法:
/**
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
由此可見(jiàn)threadlocalmap邏輯上來(lái)說(shuō)為一個(gè)環(huán)形践啄。
set方法:
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//key所在的槽位浇雹,使用該key的哈希值與上tab表的長(zhǎng)度減一,相當(dāng)于用len對(duì)threadLocalHashCode取模
int i = key.threadLocalHashCode & (len-1);
//若槽不為空屿讽,線(xiàn)性探測(cè)
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//如果槽中entry對(duì)應(yīng)的threadlocal對(duì)象與參數(shù)所傳入對(duì)象相同昭灵,則直接替換
if (k == key) {
e.value = value;
return;
}
//若槽中entry弱引用指向的threadlocal對(duì)象已經(jīng)被gc回收,則該entry失效伐谈,替換
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//否則new一個(gè)entry
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
未完待續(xù)