国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

ThreadLocal 源碼剖析

2019-11-15 00:13:17
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
ThreadLocal 源碼剖析

ThreadLocal是java語(yǔ)言提供的用于支持線程局部變量的類(lèi)。所謂的線程局部變量,就是僅僅只能被本線程訪問(wèn),不能在線程之間進(jìn)行共享訪問(wèn)的變量(每個(gè)線程一個(gè)拷貝)。在各個(gè)Java web的各種框架中ThreadLocal幾乎已經(jīng)被用爛了,sPRing中有使用,mybatis中也有使用,hibernate中也有使用,甚至我們寫(xiě)個(gè)分頁(yè)也用ThreadLocal來(lái)傳遞參數(shù)......這也從側(cè)面說(shuō)明了ThreadLocal十分的給力。

從使用者的角度而言,一般我們可以將ThreadLocal看做是一個(gè):ConcurrentHashMap<Thread, Object>,以Thread引用為key, 來(lái)保存本線程的局部變量。但是從實(shí)現(xiàn)的角度而言,ThreadLocal的實(shí)現(xiàn)根本就不是這樣的。下面從源碼分析ThreadLocal的實(shí)現(xiàn)。

1. 既然是線程局部變量,那么理所當(dāng)然就應(yīng)該存儲(chǔ)在自己的線程對(duì)象中,我們可以從 Thread 的源碼中找到線程局部變量存儲(chǔ)的地方:

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;    /*     * InheritableThreadLocal values pertaining to this thread. This map is     * maintained by the InheritableThreadLocal class.     */    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

我們可以看到線程局部變量是存儲(chǔ)在Thread對(duì)象的 threadLocals 屬性中,而 threadLocals 屬性是一個(gè) ThreadLocal.ThreadLocalMap 對(duì)象。

2. 我們接著看 ThreadLocal.ThreadLocalMap 是何方神圣

    /**     * ThreadLocalMap is a customized hash map suitable only for     * maintaining thread local values. No Operations are exported     * outside of the ThreadLocal class. The class is package private to     * allow declaration of fields in class Thread.  To help deal with     * very large and long-lived usages, the hash table entries use     * WeakReferences for keys. However, since reference queues are not     * used, stale entries are guaranteed to be removed only when     * the table starts running out of space.     */    static class ThreadLocalMap {        /**         * The entries in this hash map extend WeakReference, using         * its main ref field as the key (which is always a         * ThreadLocal object).  Note that null keys (i.e. entry.get()         * == null) mean that the key is no longer referenced, so the         * entry can be expunged from table.  Such entries are referred to         * as "stale entries" in the code that follows.         */        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;        // ... ...        /**         * Construct a new map initially containing (firstKey, firstValue).         * ThreadLocalMaps are constructed lazily, so we only create         * one when we have at least one entry to put in it.         */        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);        }

可以看到ThreadLocal.ThreadLocalMap 是 ThreadLocal 的一個(gè)靜態(tài)內(nèi)部類(lèi)。ThreadLocalMap從字面上就可以看出這是一個(gè)保存ThreadLocal對(duì)象的map(其實(shí)是以它為Key),沒(méi)錯(cuò),不過(guò)是經(jīng)過(guò)了兩層包裝的ThreadLocal對(duì)象。第一層包裝是使用 WeakReference<ThreadLocal<?>> 將ThreadLocal對(duì)象變成一個(gè)弱引用的對(duì)象;第二層包裝是 定義了一個(gè)專(zhuān)門(mén)的類(lèi) Entry 來(lái)擴(kuò)展WeakReference<ThreadLocal<?>>:

        static class Entry extends WeakReference<ThreadLocal<?>> {            /** The value associated with this ThreadLocal. */            Object value;            Entry(ThreadLocal<?> k, Object v) {                super(k);                value = v;            }        }

類(lèi) Entry 很顯然是一個(gè)保存map鍵值對(duì)的實(shí)體,ThreadLocal<?>為key, 要保存的線程局部變量的值為value。super(k)調(diào)用的WeakReference的構(gòu)造函數(shù),表示將ThreadLocal<?>對(duì)象轉(zhuǎn)換成弱引用對(duì)象,用做key。

從 ThreadLocalMap 的構(gòu)造函數(shù):

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

可以看出,ThreadLocalMap這個(gè)map的實(shí)現(xiàn)是使用一個(gè)數(shù)組 private Entry[] table 來(lái)保存鍵值對(duì)的實(shí)體,初始大小為16,ThreadLocalMap自己實(shí)現(xiàn)了如何從 key 到 value 的映射: firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)

    /**     * ThreadLocals rely on per-thread linear-probe hash maps attached     * to each thread (Thread.threadLocals and     * inheritableThreadLocals).  The ThreadLocal objects act as keys,     * searched via threadLocalHashCode.  This is a custom hash code     * (useful only within ThreadLocalMaps) that eliminates collisions     * in the common case where consecutively constructed ThreadLocals     * are used by the same threads, while remaining well-behaved in     * less common cases.     */    private final int threadLocalHashCode = nextHashCode();    /**     * The next hash code to be given out. Updated atomically. Starts at     * zero.     */    private static AtomicInteger nextHashCode = new AtomicInteger();    /**     * The difference between successively generated hash codes - turns     * implicit sequential thread-local IDs into near-optimally spread     * multiplicative hash values for power-of-two-sized tables.     */    private static final int HASH_INCREMENT = 0x61c88647;    /**     * Returns the next hash code.     */    private static int nextHashCode() {        return nextHashCode.getAndAdd(HASH_INCREMENT);    }

使用一個(gè) static 的原子屬性 AtomicInteger nextHashCode,通過(guò)每次增加 HASH_INCREMENT = 0x61c88647 ,然后 & (INITIAL_CAPACITY - 1) 取得在數(shù)組 private Entry[] table 中的索引。

3. 我們先看一下 Thread 對(duì)象中的 ThreadLocal.ThreadLocalMap threadLocals = null; 如何初始化:

    /**     * Sets the current thread's copy of this thread-local variable     * to the specified value.  Most subclasses will have no need to     * override this method, relying solely on the {@link #initialValue}     * method to set the values of thread-locals.     *     * @param value the value to be stored in the current thread's copy of     *        this thread-local.     */    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }    /**     * Get the map associated with a ThreadLocal. Overridden in     * InheritableThreadLocal.     *     * @param  t the current thread     * @return the map     */    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }    /**     * Create the map associated with a ThreadLocal. Overridden in     * InheritableThreadLocal.     *     * @param t the current thread     * @param firstValue value for the initial entry of the map     */    void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }

ThreadLocal在調(diào)用set方法時(shí),如果 getMap(注意是以Thread引用為key) 返回的 t.threadLocals 為null,那么表示該線程的 ThreadLocalMap 還沒(méi)有初始化,所以調(diào)用createMap進(jìn)行初始化:t.threadLocals = new ThreadLocalMap(this, firstValue);

注意這里使用到了延遲初始化的技術(shù):

        /**         * Construct a new map initially containing (firstKey, firstValue).         * ThreadLocalMaps are constructed lazily, so we only create         * one when we have at least one entry to put in it.         */        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);        }

這里僅僅是初始化了16個(gè)元素的引用數(shù)組,并沒(méi)有初始化16個(gè) Entry 對(duì)象。而是一個(gè)線程中有多少個(gè)線程局部對(duì)象要保存,那么就初始化多少個(gè) Entry 對(duì)象來(lái)保存它們。

到了這里,我們可以思考一下,為什么要這樣實(shí)現(xiàn)了。為什么要用 ThreadLocalMap 來(lái)保存線程局部對(duì)象呢?原因是一個(gè)線程擁有的的局部對(duì)象可能有很多,這樣實(shí)現(xiàn)的話(huà),那么不管你一個(gè)線程擁有多少個(gè)局部變量,都是使用同一個(gè) ThreadLocalMap 來(lái)保存的,ThreadLocalMap 中 private Entry[] table 的初始大小是16。超過(guò)容量的2/3時(shí),會(huì)擴(kuò)容。

4. 我們?cè)诳匆幌?ThreadLocal.set 方法:

    /**     * Sets the current thread's copy of this thread-local variable     * to the specified value.  Most subclasses will have no need to     * override this method, relying solely on the {@link #initialValue}     * method to set the values of thread-locals.     *     * @param value the value to be stored in the current thread's copy of     *        this thread-local.     */    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

我們看到是以當(dāng)前 thread 的引用為 key, 獲得 ThreadLocalMap ,然后調(diào)用 map.set(this, value); 保存進(jìn) private Entry[] table :

        /**         * Set the value associated with key.         * @param key the thread local object         * @param value the value to be set         */        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();        }

5. ThreadLocal 涉及到的兩個(gè)層面的內(nèi)存自動(dòng)回收

1)在 ThreadLocal 層面的內(nèi)存回收:

/* * Each thread holds an implicit reference to its copy of a thread-local * variable as long as the thread is alive and the {@code 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).

當(dāng)線程死亡時(shí),那么所有的保存在的線程局部變量就會(huì)被回收,其實(shí)這里是指線程Thread對(duì)象中的 ThreadLocal.ThreadLocalMap threadLocals 會(huì)被回收,這是顯然的。

2)ThreadLocalMap 層面的內(nèi)存回收:

    /**     * ThreadLocalMap is a customized hash map suitable only for     * maintaining thread local values. No operations are exported     * outside of the ThreadLocal class. The class is package private to     * allow declaration of fields in class Thread.  To help deal with     * very large and long-lived usages, the hash table entries use     * WeakReferences for keys. However, since reference queues are not     * used, stale entries are guaranteed to be removed only when     * the table starts running out of space.     */

如果線程可以活很長(zhǎng)的時(shí)間,并且該線程保存的線程局部變量有很多(也就是 Entry 對(duì)象很多),那么就涉及到在線程的生命期內(nèi)如何回收 ThreadLocalMap 的內(nèi)存了,不然的話(huà),Entry對(duì)象越多,那么ThreadLocalMap 就會(huì)越來(lái)越大,占用的內(nèi)存就會(huì)越來(lái)越多,所以對(duì)于已經(jīng)不需要了的線程局部變量,就應(yīng)該清理掉其對(duì)應(yīng)的Entry對(duì)象。使用的方式是,Entry對(duì)象的key是WeakReference 的包裝,當(dāng)ThreadLocalMap 的 private Entry[] table,已經(jīng)被占用達(dá)到了三分之二時(shí) threshold = 2/3(也就是線程擁有的局部變量超過(guò)了10個(gè)) ,就會(huì)嘗試回收 Entry 對(duì)象,我們可以看到 ThreadLocalMap.set方法中有下面的代碼:

            if (!cleanSomeSlots(i, sz) && sz >= threshold)                rehash();

cleanSomeSlots 就是進(jìn)行回收內(nèi)存:

        /**         * Heuristically scan some cells looking for stale entries.         * This is invoked when either a new element is added, or         * another stale one has been expunged. It performs a         * logarithmic number of scans, as a balance between no         * scanning (fast but retains garbage) and a number of scans         * proportional to number of elements, that would find all         * garbage but would cause some insertions to take O(n) time.         *         * @param i a position known NOT to hold a stale entry. The         * scan starts at the element after i.         *         * @param n scan control: {@code log2(n)} cells are scanned,         * unless a stale entry is found, in which case         * {@code log2(table.length)-1} additional cells are scanned.         * When called from insertions, this parameter is the number         * of elements, but when from replaceStaleEntry, it is the         * table length. (Note: all this could be changed to be either         * more or less aggressive by weighting n instead of just         * using straight log n. But this version is simple, fast, and         * seems to work well.)         *         * @return true if any stale entries have been removed.         */        private boolean cleanSomeSlots(int i, int n) {            boolean removed = false;            Entry[] tab = table;            int len = tab.length;            do {                i = nextIndex(i, len);                Entry e = tab[i];                if (e != null && e.get() == null) {                    n = len;                    removed = true;                    i = expungeStaleEntry(i);                }            } while ( (n >>>= 1) != 0);            return removed;        }
e.get() == null 調(diào)用的是 Entry 的父類(lèi) WeakReference<ThreadLocal<?>> 的方法:
    /**     * Returns this reference object's referent.  If this reference object has     * been cleared, either by the program or by the garbage collector, then     * this method returns <code>null</code>.     *     * @return   The object to which this reference refers, or     *           <code>null</code> if this reference object has been cleared     */    public T get() {        return this.referent;    }

返回 null ,表示 Entry 的 key 已經(jīng)被回收了,所以可以回收該 Entry 對(duì)象了:expungeStaleEntry(i)

        /**         * Expunge a stale entry by rehashing any possibly colliding entries         * lying between staleSlot and the next null slot.  This also expunges         * any other stale entries encountered before the trailing null.  See         * Knuth, Section 6.4         *         * @param staleSlot index of slot known to have null key         * @return the index of the next null slot after staleSlot         * (all between staleSlot and this slot will have been checked         * for expunging).         */        private int expungeStaleEntry(int staleSlot) {            Entry[] tab = table;            int len = tab.length;            // expunge entry at staleSlot            tab[staleSlot].value = null;            tab[staleSlot] = null;            size--;

6. ThreadLocal常用的接口:

1)需要制定初始值時(shí),可以覆蓋protected T initialValue()方法;

2)public T get();

3)public void set(T value);

4)public void remove();

7. 總結(jié)

1)一個(gè)線程中的所有的局部變量其實(shí)存儲(chǔ)在該線程自己的同一個(gè)map屬性中;

2)線程死亡時(shí),線程局部變量會(huì)自動(dòng)回收內(nèi)存;

3)線程局部變量時(shí)通過(guò)一個(gè) Entry 保存在map中,該Entry 的key是一個(gè) WeakReference包裝的ThreadLocal, value為線程局部變量;

key 到 value 的映射是通過(guò):ThreadLocal.threadLocalHashCode & (INITIAL_CAPACITY - 1) 來(lái)完成的;

4)當(dāng)線程擁有的局部變量超過(guò)了容量的2/3(沒(méi)有擴(kuò)大容量時(shí)是10個(gè)),會(huì)涉及到ThreadLocalMap中Entry的回收;


發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 湟中县| 哈密市| 崇州市| 赫章县| 开远市| 济源市| 汨罗市| 琼海市| 双江| 青浦区| 鄢陵县| 临颍县| 兰溪市| 建平县| 临武县| 三都| 黄龙县| 桂平市| 五大连池市| 潼南县| 准格尔旗| 革吉县| 庄河市| 海门市| 莆田市| 云阳县| 德庆县| 丹东市| 桦南县| 聊城市| 图们市| 明光市| 治多县| 株洲市| 金寨县| 古田县| 屏边| 左云县| 安图县| 中卫市| 忻城县|