Java ThreadLocal 原理分析

ThreadLocal提供了線程本地變量,它可以保證訪問(wèn)到的變量屬于當(dāng)前線程,每個(gè)線程都保存有一個(gè)變量副本,每個(gè)線程的變量都不同。ThreadLocal相當(dāng)于提供了一種線程隔離,將變量與線程相綁定。

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

ThreadLocal 類(lèi)定義如下:

public class ThreadLocal<T> {

    private final int threadLocalHashCode = nextHashCode();

    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    private static final int HASH_INCREMENT = 0x61c88647;

    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    
    public ThreadLocal() {
    }
}

ThreadLocal通過(guò)threadLocalHashCode來(lái)標(biāo)識(shí)每一個(gè)ThreadLocal的唯一性。threadLocalHashCode通過(guò)CAS操作進(jìn)行更新,每次hash操作的增量為 0x61c88647(不知為何)。

接下來(lái),看看它的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);
    }

通過(guò)Thread.currentThread()方法獲取了當(dāng)前的線程引用,并傳給了getMap(Thread)方法獲取一個(gè)ThreadLocalMap的實(shí)例。我們繼續(xù)跟進(jìn)getMap(Thread)方法:

 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

可以看到getMap(Thread)方法直接返回Thread實(shí)例的成員變量threadLocals。它的定義在Thread內(nèi)部,訪問(wèn)級(jí)別為package級(jí)別:


public class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    
}

到了這里,我們可以看出,每個(gè)Thread里面都有一個(gè)ThreadLocal.ThreadLocalMap成員變量,也就是說(shuō)每個(gè)線程通過(guò)ThreadLocal.ThreadLocalMap與ThreadLocal相綁定,這樣可以確保每個(gè)線程訪問(wèn)到的thread-local variable都是本線程的。

我們往下繼續(xù)分析。獲取了ThreadLocalMap實(shí)例以后,如果它不為空則調(diào)用ThreadLocalMap.ThreadLocalMap 的set方法設(shè)值;若為空則調(diào)用ThreadLocal 的createMap方法new一個(gè)ThreadLocalMap實(shí)例并賦給Thread.threadLocals。

ThreadLocal 的 createMap方法的源碼如下:

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocal 的 get 方法,源碼如下:

    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();
    }

通過(guò)Thread.currentThread()方法獲取了當(dāng)前的線程引用,并傳給了getMap(Thread)方法獲取一個(gè)ThreadLocalMap的實(shí)例。繼續(xù)跟進(jìn)setInitialValue()方法:

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

首先調(diào)用 initialValue()方法來(lái)初始化,然后 通過(guò)Thread.currentThread()方法獲取了當(dāng)前的線程引用,并傳給了getMap(Thread)方法獲取一個(gè)ThreadLocalMap的實(shí)例,并將 初始化值存到ThreadLocalMap 中。

initialValue() 源碼如下:

  protected T initialValue() {
        return null;
    }

下面我們探究一下ThreadLocalMap的實(shí)現(xiàn)。

ThreadLocalMap

ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類(lèi),源碼如下:

public class ThreadLocal<T> {

    static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0

        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);
        }
    }
    
}

其中INITIAL_CAPACITY代表這個(gè)Map的初始容量;1是一個(gè)Entry類(lèi)型的數(shù)組,用于存儲(chǔ)數(shù)據(jù);size代表表中的存儲(chǔ)數(shù)目;threshold代表需要擴(kuò)容時(shí)對(duì)應(yīng)size的閾值。

Entry類(lèi)是ThreadLocalMap的靜態(tài)內(nèi)部類(lèi),用于存儲(chǔ)數(shù)據(jù)。

Entry類(lèi)繼承了WeakReference<ThreadLocal<?>>,即每個(gè)Entry對(duì)象都有一個(gè)ThreadLocal的弱引用(作為key),這是為了防止內(nèi)存泄露。一旦線程結(jié)束,key變?yōu)橐粋€(gè)不可達(dá)的對(duì)象,這個(gè)Entry就可以被GC了。

接下來(lái)我們來(lái)看ThreadLocalMap 的set方法的實(shí)現(xiàn):

        private void set(ThreadLocal key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            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) {
                    e.value = value;
                    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();
        }

ThreadLocal 的get方法會(huì)調(diào)用 ThreadLocalMap 的 getEntry(ThreadLocal key) ,其源碼如下:

    private Entry getEntry(ThreadLocal key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }

    private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
        Entry[] tab = table;
        int len = tab.length;

        while (e != null) {
            ThreadLocal k = e.get();
            if (k == key)
                return e;
            if (k == null)
                expungeStaleEntry(i);
            else
                i = nextIndex(i, len);
            e = tab[i];
        }
        return null;
    }

參考資料

并發(fā)編程 | ThreadLocal源碼深入分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容