ThreadLocal In Java 8

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),我們抓住主要矛盾從setget方法開(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ù),ThreadLocalMapThreadLocal的靜態(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)系就清楚了:

relationship.png

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容