java.lang.ThreadLocal 源碼解析

原文發(fā)表于:http://blog.csdn.net/qq_27485935 , 大家沒事可以去逛逛 (? ??_??)?

ThreadLocal

概述: 位于 java.lang 包下, 是一個線程內(nèi)部的數(shù)據(jù)存儲類, 通過它可以在指定線程中存儲數(shù)據(jù), 數(shù)據(jù)存儲之后, 只有在指定線程中才可以獲取到存儲的數(shù)據(jù)。

使用場景: Looper、 ActivityThread

基本用法:

mThreadLocal = new ThreadLocal<Integer>();
mThreadLocal.set(10);  // 通過 set 方法設(shè)值
mThreadLocal.get();  // 通過 get 方法取值

源碼解析

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

① ThreadLocal#getMap

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

我們一步步分析, 首先通過 getMap() 方法獲取 Thread 內(nèi)部 的 threadLocals 對象。 而 ThreadLocalMap 為 ThreadLocal 內(nèi)部的靜態(tài)類。

ThreadLocalMap

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;
        }
    }
    
    private Entry[] table;
    
    
    private void set(ThreadLocal key, Object value) {
            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();
        }
}

源碼可見, ThreadLocalMap 通過 Entry 數(shù)組來保存鍵值對的, 并且對鍵也是弱引用狀態(tài)。 set 一個數(shù)值, 將會取出鍵(ThreadLocal) 中的 threadLocalHashCode 值, 而 threadLocalHashCode 是唯一的, 所以可以通過它來確定數(shù)值在 Entry 數(shù)組中的位置。

所以回到剛剛的 ThreadLocal#set 方法。 先從 Thread 類中取出維持線程本地數(shù)據(jù)的 ThreadLocalMap 類, 若 map 對象不為空, 直接將 ThreadLocal 和數(shù)據(jù)作為參數(shù) set 到 map 中維持的 table 數(shù)組中。 若 map 對象為空, 調(diào)用 createMap 方法, 創(chuàng)建 ThreadLocalMap 對象, 設(shè)入數(shù)據(jù)并將該對象設(shè)置到 Thread 對象當中。 以下為 createMap 源碼。

ThreadLocal#createMap

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

接下來分析 get 方法

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

同樣, 獲取當前線程對象, 并獲取維持線程本地數(shù)據(jù)的 threadLocals, 以 ThreadLocal 對象取出對應(yīng)數(shù)值對象。 如果對象不為空, 就強制轉(zhuǎn)化為泛型 T 對應(yīng)的類型。 若 map 為空, 或者鍵值對取出數(shù)據(jù)對象為空, 就調(diào)用 setInitialValue 方法, 以下代碼就不解釋了。

ThreadLocal#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;
}

protected T initialValue() {
    return null;
}

從 ThreadLocal 的 setget 方法可以看出, 它們所操作的對象都是當前線程的 threadLocals 對象中的 table 數(shù)組, 因此在不同線程中訪問同一個 ThreadLocal 的 setget 方法, 他們對 ThreadLocal 所做的讀/寫操作僅限于各個線程的內(nèi)部, 獲取的值也就不同了。

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

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

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