ThreadLocal是什么?
ThreadLocalThreadLocal是一個(gè)線程內(nèi)部用于存儲(chǔ)數(shù)據(jù)的類,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后,只有在該線程中可以獲取到存儲(chǔ)的數(shù)據(jù),對(duì)于其它線程來說無法獲取到數(shù)據(jù)。個(gè)人認(rèn)為是一個(gè)線程內(nèi)部的存儲(chǔ)機(jī)制。
如何使用?
ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
這樣就在一個(gè)線程創(chuàng)建了ThreadLocal這個(gè)對(duì)象,這個(gè)對(duì)象支持范性的,也就是說我們可以存儲(chǔ)的自己想存的任意類型。
threadLocal.set(true);
這樣我們就在當(dāng)前所在線程,存儲(chǔ)了true。
new Thread("Thread#1") {
@Override
public void run() {
System.out.println(threadLocal.get());
};
}.start();
我們?cè)谝粋€(gè)分線程去打印這個(gè)剛才存儲(chǔ)的值會(huì)發(fā)現(xiàn)是null,因?yàn)檫@是在兩個(gè)線程操作的。如果在當(dāng)前線程獲取則為正確剛才存的值。
如果想移除這個(gè)數(shù)據(jù)也很簡單:
threadLocal.remove();
這就是threadLocal的使用,其實(shí)很簡單。 主要記得是區(qū)分線程的就ok。
內(nèi)部源碼是如何實(shí)現(xiàn)的呢?

這就是treadLocal中的所有方法了,其實(shí)我們最關(guān)心的就是set和get方法。
我們先看set:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
這就是set方法,對(duì)就這么幾行。
首先會(huì)通過Thread.currentThread();這個(gè)方法獲取當(dāng)前線程的Thread。然后通過getMap()獲取 ThreadLocalMap對(duì)象。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
getMap中也很簡單,就是返回這個(gè)線程的threadLocals, 這個(gè)ThreadLocal就是 Thread中的一個(gè)變量ThreadLocal.ThreadLocalMap threadLocals = null;
其實(shí)還是ThreadLocal中的內(nèi)部類ThreadLocalMap;這里一會(huì)在分析,先順著思路往下走。
獲取map之后會(huì)根據(jù)判斷如果不是null就進(jìn)行set,也就是存儲(chǔ),如果是null就會(huì)調(diào)用createMap()方法進(jìn)行創(chuàng)建這個(gè)ThreadMap。接下來在看是如何set和createMap的。
創(chuàng)建其實(shí)很簡單,直接就是new一個(gè)。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
set方法就稍微復(fù)雜了些,因?yàn)檫@也是核心內(nèi)容。set方法在內(nèi)部類ThreadLocalMap中,所以接下來分析下這個(gè)ThreadLocalMap類。
ThreadLocalMap
這個(gè)類在構(gòu)造中創(chuàng)建了一個(gè)數(shù)組, new Entry[INITIAL_CAPACITY]; ,Entry里面就是一個(gè)object的對(duì)象,然后里面主要getEntry和set方法進(jìn)行存取和讀取。
private void set(ThreadLocal key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
if (k == key) {//這里判斷這個(gè)k和傳進(jìn)來的key是否相等
e.value = value;//進(jìn)行存儲(chǔ)后return
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
看主要代碼 判斷k和傳進(jìn)來的key相等話就存儲(chǔ)這個(gè)value
get方法:
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);//根據(jù)key獲取位下標(biāo)
Entry e = table[i]; // 根據(jù)下標(biāo),獲取這個(gè)Entry 里面是一個(gè)object,實(shí)現(xiàn)了軟引用
if (e != null && e.get() == key)
return e; //校驗(yàn)沒問題后返回
else
return getEntryAfterMiss(key, i, e);
}
我們?cè)倏磄et:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
get里面也是要先獲取當(dāng)前的Thread這也就是為什么ThreadLocal獲取和存儲(chǔ)的都是只在當(dāng)前線程的。
然后也是getMap方法獲取這個(gè)ThreadLocalMap,這也就是為什么里面就一行代碼,也要寫成一個(gè)方來,因?yàn)檫@是中思路,其他地方獲取直接調(diào)用就行,日后擴(kuò)展的話一樣方便。
再然后就是通過這個(gè)map去調(diào)用上面說的getEntry方法。
至此,我們就知道ThreadLocal的總體工作流程和思路了。