閱讀原文請(qǐng)?jiān)L問(wèn)我的博客BrightLoong's Blog
一. 簡(jiǎn)介
提醒篇幅較大需耐心阱佛。
簡(jiǎn)介來(lái)自ThreadLocal類注釋
ThreadLocal類提供了線程局部 (thread-local) 變量浓镜。這些變量與普通變量不同屠橄,每個(gè)線程都可以通過(guò)其 get 或 set方法來(lái)訪問(wèn)自己的獨(dú)立初始化的變量副本。ThreadLocal 實(shí)例通常是類中的 private static 字段鞍爱,它們希望將狀態(tài)與某一個(gè)線程(例如胁编,用戶 ID 或事務(wù) ID)相關(guān)聯(lián)竟痰。
下面是類注釋中給出的一個(gè)列子:
以下類生成對(duì)每個(gè)線程唯一的局部標(biāo)識(shí)符。 線程 ID 是在第一次調(diào)用 UniqueThreadIdGenerator.getCurrentThreadId() 時(shí)分配的掏呼,在后續(xù)調(diào)用中不會(huì)更改。
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
public void run() {
System.out.print(threadId.get());
}
}).start();
}
}
}
輸出結(jié)果 :01234
只要線程是活動(dòng)的并且 ThreadLocal 實(shí)例是可訪問(wèn)的铅檩,每個(gè)線程都會(huì)保持對(duì)其線程局部變量副本的隱式引用憎夷;在線程消失之后,其線程局部實(shí)例的所有副本都會(huì)被垃圾回收(除非存在對(duì)這些副本的其他引用)昧旨。
二. 整體認(rèn)識(shí)
UML類圖
ThreadLocal中的嵌套內(nèi)部類ThreadLocalMap拾给,這個(gè)類本質(zhì)上是一個(gè)map,和HashMap之類的實(shí)現(xiàn)相似兔沃,依然是key-value的形式蒋得,其中有一個(gè)內(nèi)部類Entry,其中key可以看做是ThreadLocal實(shí)例乒疏,但是其本質(zhì)是持有ThreadLocal實(shí)例的弱引用(之后會(huì)詳細(xì)說(shuō)到)额衙。
還是說(shuō)ThreadLocalMap(下面是很大篇幅的閱讀其源碼,畢竟了解清楚了ThreadLocalMap的來(lái)龍去脈,ThreadLocal基本也就差不多了)窍侧,在ThreadLocal中并沒(méi)有對(duì)于ThreadLocalMap的引用县踢,是的,ThreadLocalMap的引用在Thread類中伟件,代碼如下硼啤。每個(gè)線程在向ThreadLocal里塞值的時(shí)候,其實(shí)都是向自己所持有的ThreadLocalMap里塞入數(shù)據(jù)斧账;讀的時(shí)候同理谴返,首先從自己線程中取出自己持有的ThreadLocalMap,然后再根據(jù)ThreadLocal引用作為key取出value咧织,基于以上描述嗓袱,ThreadLocal實(shí)現(xiàn)了變量的線程隔離(當(dāng)然,畢竟變量其實(shí)都是從自己當(dāng)前線程實(shí)例中取出來(lái)的)拯爽。
public
class Thread implements Runnable {
......
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
......
原理圖
根據(jù)理解索抓,畫出ThreadLocal原理圖如下:
- 首先,主線程定義的兩個(gè)ThreadLocal變量毯炮,和兩個(gè)子線程——線程A和線程B逼肯。
- 線程A和線程B分別持有一個(gè)ThreadLocalMap用于保存自己獨(dú)立的副本,主線程的ThreadLocal中封裝了get()和set()之類的方法桃煎。
- 在線程A和線程B中調(diào)用ThreadLocal的set方法篮幢,會(huì)首先通過(guò)getMap(Thread.currentThread)獲得線程A或者是線程B持有的ThreadLocalMap,在調(diào)用map.put()方法,并將ThreadLocal作為key为迈。
- get()方法和set()方法原理類似三椿,也是先獲取當(dāng)前調(diào)用線程的ThreadLocalMap,再?gòu)膍ap中獲取value,并將ThreadLocal作為key葫辐。
三. ThreadLocalMap源碼分析
下面一步一步介紹ThreadLocalMap源碼分析的相關(guān)源碼搜锰,在分析ThreadLocalMap的同時(shí),也會(huì)介紹與ThreadLocalMap關(guān)聯(lián)的ThreadLocal中的方法(這樣分析完ThreadLocalMap耿战,ThreadLocal基本就搞定了)蛋叼,可能有些需要前后結(jié)合才能真正理解肩杈。
成員變量
/**
* 初始容量 —— 必須是2的冥
*/
private static final int INITIAL_CAPACITY = 16;
/**
* 存放數(shù)據(jù)的table秀鞭,Entry類的定義在下面分析
* 同樣,數(shù)組長(zhǎng)度必須是2的冥涯鲁。
*/
private Entry[] table;
/**
* 數(shù)組里面entrys的個(gè)數(shù)鸭栖,可以用于判斷table當(dāng)前使用量是否超過(guò)負(fù)因子歌馍。
*/
private int size = 0;
/**
* 進(jìn)行擴(kuò)容的閾值,表使用量大于它的時(shí)候進(jìn)行擴(kuò)容晕鹊。
*/
private int threshold; // Default to 0
/**
* 定義為長(zhǎng)度的2/3
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
各個(gè)值的含義已經(jīng)在注釋里面說(shuō)了松却,就不再一一解釋暴浦。
存儲(chǔ)結(jié)構(gòu)——Entry
/**
* Entry繼承WeakReference,并且用ThreadLocal作為key.如果key為null
* (entry.get() == null)表示key不再被引用玻褪,表示ThreadLocal對(duì)象被回收
* 因此這時(shí)候entry也可以從table從清除肉渴。
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry繼承WeakReference,使用弱引用,可以將ThreadLocal對(duì)象的生命周期和線程生命周期解綁带射,持有對(duì)ThreadLocal的弱引用同规,可以使得ThreadLocal在沒(méi)有其他強(qiáng)引用的時(shí)候被回收掉,這樣可以避免因?yàn)榫€程得不到銷毀導(dǎo)致ThreadLocal對(duì)象無(wú)法被回收窟社。
關(guān)于WeakReference可以參考我之前的博客券勺,關(guān)于Java中的WeakReferencea。
ThreadLocalMap的set()方法和Hash映射
要了解ThreadLocalMap中Hash映射的方式灿里,首先從ThreadLocal的set()方法入手关炼,逐層深入。
ThreadLocal中的set()
先看看ThreadLocal中set()源碼匣吊。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocal.ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal.ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
}
- 代碼很簡(jiǎn)單儒拂,獲取當(dāng)前線程,并獲取當(dāng)前線程的ThreadLocalMap實(shí)例(從getMap(Thread t)中很容易看出來(lái))色鸳。
- 如果獲取到的map實(shí)例不為空社痛,調(diào)用map.set()方法,否則調(diào)用構(gòu)造函數(shù) ThreadLocal.ThreadLocalMap(this, firstValue)實(shí)例化map命雀。
可以看出來(lái)線程中的ThreadLocalMap使用的是延遲初始化蒜哀,在第一次調(diào)用get()或者set()方法的時(shí)候才會(huì)進(jìn)行初始化。下面來(lái)看看構(gòu)造函數(shù)ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue)
吏砂。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化table
table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
//計(jì)算索引
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//設(shè)置值
table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
size = 1;
//設(shè)置閾值
setThreshold(INITIAL_CAPACITY);
}
主要說(shuō)一下計(jì)算索引撵儿,firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)
。
- 關(guān)于
& (INITIAL_CAPACITY - 1)
,這是取模的一種方式狐血,對(duì)于2的冪作為模數(shù)取模淀歇,用此代替%(2^n)
,這也就是為啥容量必須為2的冥匈织,在這個(gè)地方也得到了解答房匆,至于為什么可以這樣這里不過(guò)多解釋,原理很簡(jiǎn)單报亩。 - 關(guān)于
firstKey.threadLocalHashCode
:
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
定義了一個(gè)AtomicInteger類型,每次獲取當(dāng)前值并加上HASH_INCREMENT井氢,HASH_INCREMENT = 0x61c88647
,關(guān)于這個(gè)值和斐波那契散列
有關(guān)弦追,其原理這里不再深究,感興趣可自行搜索花竞,其主要目的就是為了讓哈希碼能均勻的分布在2的n次方的數(shù)組里, 也就是Entry[] table
中劲件。
在了解了上面的源碼后掸哑,終于能進(jìn)入正題了,下面開(kāi)始進(jìn)入ThreadLocalMap中的set()零远。
ThreadLocalMap中的set()
ThreadLocalMap使用線性探測(cè)法
來(lái)解決哈希沖突苗分,線性探測(cè)法的地址增量di = 1, 2, ... , m-1,其中牵辣,i為探測(cè)次數(shù)摔癣。該方法一次探測(cè)下一個(gè)地址,直到有空的地址后插入纬向,若整個(gè)空間都找不到空余的地址择浊,則產(chǎn)生溢出。假設(shè)當(dāng)前table長(zhǎng)度為16逾条,也就是說(shuō)如果計(jì)算出來(lái)key的hash值為14琢岩,如果table[14]上已經(jīng)有值,并且其key與當(dāng)前key不一致师脂,那么就發(fā)生了hash沖突担孔,這個(gè)時(shí)候?qū)?4加1得到15,取table[15]進(jìn)行判斷吃警,這個(gè)時(shí)候如果還是沖突會(huì)回到0糕篇,取table[0],以此類推,直到可以插入汤徽。
按照上面的描述娩缰,可以把table看成一個(gè)環(huán)形數(shù)組
。
先看一下線性探測(cè)相關(guān)的代碼谒府,從中也可以看出來(lái)table實(shí)際是一個(gè)環(huán):
/**java
/**
* 獲取環(huán)形數(shù)組的下一個(gè)索引
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* 獲取環(huán)形數(shù)組的上一個(gè)索引
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
ThreadLocalMap的set()及其set()相關(guān)代碼如下:
private void set(ThreadLocal<?> key, Object value) {
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
//計(jì)算索引拼坎,上面已經(jīng)有說(shuō)過(guò)。
int i = key.threadLocalHashCode & (len-1);
/**
* 根據(jù)獲取到的索引進(jìn)行循環(huán)完疫,如果當(dāng)前索引上的table[i]不為空泰鸡,在沒(méi)有return的情況下,
* 就使用nextIndex()獲取下一個(gè)(上面提到到線性探測(cè)法)壳鹤。
*/
for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//table[i]上key不為空盛龄,并且和當(dāng)前key相同,更新value
if (k == key) {
e.value = value;
return;
}
/**
* table[i]上的key為空芳誓,說(shuō)明被回收了(上面的弱引用中提到過(guò))余舶。
* 這個(gè)時(shí)候說(shuō)明改table[i]可以重新使用,用新的key-value將其替換,并刪除其他無(wú)效的entry
*/
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//找到為空的插入位置锹淌,插入值匿值,在為空的位置插入需要對(duì)size進(jìn)行加1操作
tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value);
int sz = ++size;
/**
* cleanSomeSlots用于清除那些e.get()==null,也就是table[index] != null && table[index].get()==null
* 之前提到過(guò)赂摆,這種數(shù)據(jù)key關(guān)聯(lián)的對(duì)象已經(jīng)被回收挟憔,所以這個(gè)Entry(table[index])可以被置null钟些。
* 如果沒(méi)有清除任何entry,并且當(dāng)前使用量達(dá)到了負(fù)載因子所定義(長(zhǎng)度的2/3),那么進(jìn)行rehash()
*/
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
/**
* 替換無(wú)效entry
*/
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
int staleSlot) {
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
ThreadLocal.ThreadLocalMap.Entry e;
/**
* 根據(jù)傳入的無(wú)效entry的位置(staleSlot),向前掃描
* 一段連續(xù)的entry(這里的連續(xù)是指一段相鄰的entry并且table[i] != null),
* 直到找到一個(gè)無(wú)效entry绊谭,或者掃描完也沒(méi)找到
*/
int slotToExpunge = staleSlot;//之后用于清理的起點(diǎn)
for (int i = prevIndex(staleSlot, len);
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i;
/**
* 向后掃描一段連續(xù)的entry
*/
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
/**
* 如果找到了key政恍,將其與傳入的無(wú)效entry替換,也就是與table[staleSlot]進(jìn)行替換
*/
if (k == key) {
e.value = value;
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
//如果向前查找沒(méi)有找到無(wú)效entry达传,則更新slotToExpunge為當(dāng)前值i
if (slotToExpunge == staleSlot)
slotToExpunge = i;
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
/**
* 如果向前查找沒(méi)有找到無(wú)效entry篙耗,并且當(dāng)前向后掃描的entry無(wú)效,則更新slotToExpunge為當(dāng)前值i
*/
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
/**
* 如果沒(méi)有找到key,也就是說(shuō)key之前不存在table中
* 就直接最開(kāi)始的無(wú)效entry——tab[staleSlot]上直接新增即可
*/
tab[staleSlot].value = null;
tab[staleSlot] = new ThreadLocal.ThreadLocalMap.Entry(key, value);
/**
* slotToExpunge != staleSlot,說(shuō)明存在其他的無(wú)效entry需要進(jìn)行清理趟大。
*/
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
/**
* 連續(xù)段清除
* 根據(jù)傳入的staleSlot,清理對(duì)應(yīng)的無(wú)效entry——table[staleSlot],
* 并且根據(jù)當(dāng)前傳入的staleSlot,向后掃描一段連續(xù)的entry(這里的連續(xù)是指一段相鄰的entry并且table[i] != null),
* 對(duì)可能存在hash沖突的entry進(jìn)行rehash鹤树,并且清理遇到的無(wú)效entry.
*
* @param staleSlot key為null,需要無(wú)效entry所在的table中的索引
* @return 返回下一個(gè)為空的solt的索引。
*/
private int expungeStaleEntry(int staleSlot) {
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
// 清理無(wú)效entry逊朽,置空
tab[staleSlot].value = null;
tab[staleSlot] = null;
//size減1罕伯,置空后table的被使用量減1
size--;
ThreadLocal.ThreadLocalMap.Entry e;
int i;
/**
* 從staleSlot開(kāi)始向后掃描一段連續(xù)的entry
*/
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
//如果遇到key為null,表示無(wú)效entry,進(jìn)行清理.
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
//如果key不為null,計(jì)算索引
int h = k.threadLocalHashCode & (len - 1);
/**
* 計(jì)算出來(lái)的索引——h叽讳,與其現(xiàn)在所在位置的索引——i不一致追他,置空當(dāng)前的table[i]
* 從h開(kāi)始向后線性探測(cè)到第一個(gè)空的slot,把當(dāng)前的entry挪過(guò)去岛蚤。
*/
if (h != i) {
tab[i] = null;
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
//下一個(gè)為空的solt的索引邑狸。
return i;
}
/**
* 啟發(fā)式的掃描清除,掃描次數(shù)由傳入的參數(shù)n決定
*
* @param i 從i向后開(kāi)始掃描(不包括i涤妒,因?yàn)樗饕秊閕的Slot肯定為null)
*
* @param n 控制掃描次數(shù)单雾,正常情況下為 log2(n) ,
* 如果找到了無(wú)效entry她紫,會(huì)將n重置為table的長(zhǎng)度len,進(jìn)行段清除硅堆。
*
* map.set()點(diǎn)用的時(shí)候傳入的是元素個(gè)數(shù),replaceStaleEntry()調(diào)用的時(shí)候傳入的是table的長(zhǎng)度len
*
* @return true if any stale entries have been removed.
*/
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
ThreadLocal.ThreadLocalMap.Entry e = tab[i];
if (e != null && e.get() == null) {
//重置n為len
n = len;
removed = true;
//依然調(diào)用expungeStaleEntry來(lái)進(jìn)行無(wú)效entry的清除
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);//無(wú)符號(hào)的右移動(dòng)贿讹,可以用于控制掃描次數(shù)在log2(n)
return removed;
}
private void rehash() {
//全清理
expungeStaleEntries();
/**
* threshold = 2/3 * len
* 所以threshold - threshold / 4 = 1en/2
* 這里主要是因?yàn)樯厦孀隽艘淮稳謇硭詓ize減小渐逃,需要進(jìn)行判斷。
* 判斷的時(shí)候把閾值調(diào)低了民褂。
*/
if (size >= threshold - threshold / 4)
resize();
}
/**
* 擴(kuò)容茄菊,擴(kuò)大為原來(lái)的2倍(這樣保證了長(zhǎng)度為2的冥)
*/
private void resize() {
ThreadLocal.ThreadLocalMap.Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
ThreadLocal.ThreadLocalMap.Entry[] newTab = new ThreadLocal.ThreadLocalMap.Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
ThreadLocal.ThreadLocalMap.Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
//雖然做過(guò)一次清理,但在擴(kuò)容的時(shí)候可能會(huì)又存在key==null的情況赊堪。
if (k == null) {
//這里試試將e.value設(shè)置為null
e.value = null; // Help the GC
} else {
//同樣適用線性探測(cè)來(lái)設(shè)置值面殖。
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
//設(shè)置新的閾值
setThreshold(newLen);
size = count;
table = newTab;
}
/**
* 全清理,清理所有無(wú)效entry
*/
private void expungeStaleEntries() {
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {
ThreadLocal.ThreadLocalMap.Entry e = tab[j];
if (e != null && e.get() == null)
//使用連續(xù)段清理
expungeStaleEntry(j);
}
}
ThreadLocalMap中的getEntry()及其相關(guān)
同樣的對(duì)于ThreadLocalMap中的getEntry()也從ThreadLocal的get()方法入手哭廉。
ThreadLocal中的get()
public T get() {
//同set方法類似獲取對(duì)應(yīng)線程中的ThreadLocalMap實(shí)例
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//為空返回初始化值
return setInitialValue();
}
/**
* 初始化設(shè)值的方法脊僚,可以被子類覆蓋。
*/
protected T initialValue() {
return null;
}
private T setInitialValue() {
//獲取初始化值群叶,默認(rèn)為null(如果沒(méi)有子類進(jìn)行覆蓋)
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//不為空不用再初始化吃挑,直接調(diào)用set操作設(shè)值
if (map != null)
map.set(this, value);
else
//第一次初始化,createMap在上面介紹set()的時(shí)候有介紹過(guò)街立。
createMap(t, value);
return value;
}
ThreadLocalMap中的getEntry()
private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> key) {
//根據(jù)key計(jì)算索引舶衬,獲取entry
int i = key.threadLocalHashCode & (table.length - 1);
ThreadLocal.ThreadLocalMap.Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
/**
* 通過(guò)直接計(jì)算出來(lái)的key找不到對(duì)于的value的時(shí)候適用這個(gè)方法.
*/
private ThreadLocal.ThreadLocalMap.Entry getEntryAfterMiss(ThreadLocal<?> key, int i, ThreadLocal.ThreadLocalMap.Entry e) {
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
//清除無(wú)效的entry
expungeStaleEntry(i);
else
//基于線性探測(cè)法向后掃描
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
ThreadLocalMap中的remove()
private void remove(ThreadLocal<?> key) {
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
//計(jì)算索引
int i = key.threadLocalHashCode & (len-1);
//進(jìn)行線性探測(cè),查找正確的key
for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
//調(diào)用weakrefrence的clear()清除引用
e.clear();
//連續(xù)段清除
expungeStaleEntry(i);
return;
}
}
}
remove()在有上面了解后可以說(shuō)極為簡(jiǎn)單了赎离,就是找到對(duì)應(yīng)的table[],調(diào)用weakrefrence的clear()清除引用逛犹,然后再調(diào)用expungeStaleEntry()進(jìn)行清除。
四. 總結(jié)
分析完ThreadLocalMap梁剔,ThreadLocal的神秘面紗也就揭開(kāi)了虽画,所以不再贅述。