TheadLocal
是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類笛园,通常用于存儲(chǔ)以線程為作用域的數(shù)據(jù)變量弥搞,避免產(chǎn)生多線程的同步問題。
記得上學(xué)時(shí)候也寫過相關(guān)源碼的分析文章医舆,但今天翻看Java 8中的ThreadLocal
類時(shí)發(fā)現(xiàn)它被重構(gòu)了竭翠,因此也重讀一下相關(guān)源碼實(shí)現(xiàn)振坚。
ThreadLocal
是一個(gè)泛型類,定義如下:
public class ThreadLocal<T>
作為存儲(chǔ)類斋扰,我們抓住主要矛盾從set
和get
方法開始分析渡八。
ThreadLocal
存數(shù)據(jù) —— set方法
public void set(T value) {
//獲取到當(dāng)前線程
Thread t = Thread.currentThread();
//根據(jù)當(dāng)前線程獲取ThreadLocalMap對象
ThreadLocalMap map = getMap(t);
if (map != null) // map對象不為空則存入對象
map.set(this, value);
else // 否則創(chuàng)建ThreadLocalMap
createMap(t, value);
}
可以看到,set方法向一個(gè)ThreadLocalMap
對象中存入數(shù)據(jù)传货,ThreadLocalMap
是ThreadLocal
的靜態(tài)內(nèi)部類呀狼,具體實(shí)現(xiàn)我們隨后進(jìn)行分析。我們現(xiàn)在只需知道它被用來存儲(chǔ)數(shù)據(jù)(像HashMap那樣):key是線程對應(yīng)的threadlocal對象损离,value是要存入的數(shù)據(jù)對象哥艇。
作者的叨叨
:其實(shí)看到這里機(jī)智點(diǎn)的小伙伴可以嘗試猜測整個(gè)ThreadLocal
類的實(shí)現(xiàn)原理。
先別管這樣的猜測是不是正確僻澎,這不重要貌踏。其實(shí)我們看源碼的時(shí)候要養(yǎng)成主動(dòng)思考的習(xí)慣,假如讓你來設(shè)計(jì)或者實(shí)現(xiàn)這樣的方案窟勃,你會(huì)怎么設(shè)計(jì)祖乳?然后再與實(shí)際源碼對比一下,并且關(guān)注一下源碼的細(xì)節(jié)秉氧。
下面我們接著看一下如何獲取之前存入的數(shù)據(jù)對象眷昆。
ThreadLocal
取數(shù)據(jù) —— get方法
public T get() {
//獲取到當(dāng)前線程
Thread t = Thread.currentThread();
//獲取ThreadLocalMap對象
ThreadLocalMap map = getMap(t);
if (map != null) {
//獲取ThreadLocalMap中的Entry對象并拿到Value
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果之前未創(chuàng)建過ThreadLocalMap,則返回null
return setInitialValue();
}
從get方法中我們可以看到汁咏,如果當(dāng)前線程之前向ThreadLocalMap
存入過數(shù)據(jù)對象亚斋,則根據(jù)線程實(shí)例獲取到存入的數(shù)據(jù)對象。
看完了存取兩個(gè)動(dòng)作攘滩,讓我們揭開ThreadLocalMap
的面紗帅刊,看看ThreadLocal
是如何存儲(chǔ)數(shù)據(jù)對象的。
ThreadLocal
儲(chǔ)數(shù)據(jù) —— ThreadLocalMap
從上面的set和get方法中都可以看到漂问,ThreadLocal一直在對ThreadLocalMap進(jìn)行操作赖瞒。我們來搞清楚Thread女揭,ThreadLocal以及ThreadLocalMap三者之間的關(guān)系。
在Thread類中可以看到栏饮,每個(gè)Thread對象中都持有一個(gè)ThreadLocalMap
成員變量:
ThreadLocal.ThreadLocalMap threadLocals = null;
在上面的set方法中我們看到了對ThreadLocalMap的創(chuàng)建:
void createMap(Thread t, T firstValue) {
//創(chuàng)建一個(gè)ThreadLocalMap對象賦值給當(dāng)前線程的成員變量threadLocals
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
從構(gòu)造函數(shù)中我們可以看到ThreadLocalMap中維護(hù)了一個(gè)Entry
對象的數(shù)組table:
private 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
對象是一種類似<ThreadLocal吧兔,Object>鍵值對的結(jié)構(gòu),每個(gè)ThreadLocal對象都對應(yīng)了各自的數(shù)據(jù)對象∨坻遥現(xiàn)在Thread境蔼,ThreadLocal以及ThreadLocalMap三者之間的關(guān)系就清楚了:
通過ThreadLocalMap和Thread的一一對應(yīng)關(guān)系實(shí)現(xiàn)了線程作用域中的數(shù)據(jù)對象存取。