TheadLocal 是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類(lèi),通常用于存儲(chǔ)以線程為作用域的數(shù)據(jù)變量,避免產(chǎn)生多線程的同步問(wèn)題。
記得上學(xué)時(shí)候也寫(xiě)過(guò)相關(guān)源碼的分析文章,但今天翻看Java 8中的ThreadLocal類(lèi)時(shí)發(fā)現(xiàn)它被重構(gòu)了,因此也重讀一下相關(guān)源碼實(shí)現(xiàn)。
ThreadLocal是一個(gè)泛型類(lèi),定義如下:
public class ThreadLocal<T>
作為存儲(chǔ)類(lèi),我們抓住主要矛盾從set和get方法開(kāi)始分析。
ThreadLocal 存數(shù)據(jù) —— set方法
public void set(T value) {
//獲取到當(dāng)前線程
Thread t = Thread.currentThread();
//根據(jù)當(dāng)前線程獲取ThreadLocalMap對(duì)象
ThreadLocalMap map = getMap(t);
if (map != null) // map對(duì)象不為空則存入對(duì)象
map.set(this, value);
else // 否則創(chuàng)建ThreadLocalMap
createMap(t, value);
}
可以看到,set方法向一個(gè)ThreadLocalMap對(duì)象中存入數(shù)據(jù),ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類(lèi),具體實(shí)現(xiàn)我們隨后進(jìn)行分析。我們現(xiàn)在只需知道它被用來(lái)存儲(chǔ)數(shù)據(jù)(像HashMap那樣):key是線程對(duì)應(yīng)的threadlocal對(duì)象,value是要存入的數(shù)據(jù)對(duì)象。
作者的叨叨:其實(shí)看到這里機(jī)智點(diǎn)的小伙伴可以嘗試猜測(cè)整個(gè)ThreadLocal類(lèi)的實(shí)現(xiàn)原理。
先別管這樣的猜測(cè)是不是正確,這不重要。其實(shí)我們看源碼的時(shí)候要養(yǎng)成主動(dòng)思考的習(xí)慣,假如讓你來(lái)設(shè)計(jì)或者實(shí)現(xiàn)這樣的方案,你會(huì)怎么設(shè)計(jì)?然后再與實(shí)際源碼對(duì)比一下,并且關(guān)注一下源碼的細(xì)節(jié)。
下面我們接著看一下如何獲取之前存入的數(shù)據(jù)對(duì)象。
ThreadLocal 取數(shù)據(jù) —— get方法
public T get() {
//獲取到當(dāng)前線程
Thread t = Thread.currentThread();
//獲取ThreadLocalMap對(duì)象
ThreadLocalMap map = getMap(t);
if (map != null) {
//獲取ThreadLocalMap中的Entry對(duì)象并拿到Value
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果之前未創(chuàng)建過(guò)ThreadLocalMap,則返回null
return setInitialValue();
}
從get方法中我們可以看到,如果當(dāng)前線程之前向ThreadLocalMap存入過(guò)數(shù)據(jù)對(duì)象,則根據(jù)線程實(shí)例獲取到存入的數(shù)據(jù)對(duì)象。
看完了存取兩個(gè)動(dòng)作,讓我們揭開(kāi)ThreadLocalMap的面紗,看看ThreadLocal是如何存儲(chǔ)數(shù)據(jù)對(duì)象的。
ThreadLocal 儲(chǔ)數(shù)據(jù) —— ThreadLocalMap
從上面的set和get方法中都可以看到,ThreadLocal一直在對(duì)ThreadLocalMap進(jìn)行操作。我們來(lái)搞清楚Thread,ThreadLocal以及ThreadLocalMap三者之間的關(guān)系。
在Thread類(lèi)中可以看到,每個(gè)Thread對(duì)象中都持有一個(gè)ThreadLocalMap成員變量:
ThreadLocal.ThreadLocalMap threadLocals = null;
在上面的set方法中我們看到了對(duì)ThreadLocalMap的創(chuàng)建:
void createMap(Thread t, T firstValue) {
//創(chuàng)建一個(gè)ThreadLocalMap對(duì)象賦值給當(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對(duì)象的數(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對(duì)象是一種類(lèi)似<ThreadLocal,Object>鍵值對(duì)的結(jié)構(gòu),每個(gè)ThreadLocal對(duì)象都對(duì)應(yīng)了各自的數(shù)據(jù)對(duì)象。現(xiàn)在Thread,ThreadLocal以及ThreadLocalMap三者之間的關(guān)系就清楚了:

通過(guò)ThreadLocalMap和Thread的一一對(duì)應(yīng)關(guān)系實(shí)現(xiàn)了線程作用域中的數(shù)據(jù)對(duì)象存取。