ThreadLocal因何而得藕

前面《Thread源碼理解》一節(jié)講了Thread的源碼妖啥,因為是第一次寫博客,難免有點拘泥于格式对碌,本來想下面這張寫一下我對線程池實現(xiàn)的理解荆虱,但是今天重新翻出了我的處女作,發(fā)現(xiàn)Thread源碼中有ThreadLocal的變量:

#1
   /* 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;

一直以來都是把ThreadLocal當作同一線程中順序存取線程變量的容器朽们,當看到Thread類中ThreadLocal變量=null且沒有使用怀读,則Thread類中定義ThreadLocals 變量是用來做什么的?后面會詳細的分析ThreadLocals變量的用法骑脱。
一菜枷、ThreadLocalMap
首先,ThreadLocalMap是一個靜態(tài)內(nèi)部類叁丧,在ThreadLocal中定義了靜態(tài)Map啤誊,此種設(shè)計屬于組合模式(六原則一法則)。

(1)
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;
            }
        }
-------------------------------------------
(2)
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
private void setThreshold(int len) {
       threshold = len * 2 / 3;
}
--------------------------------------------
(3)
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);
        }

1拥娄、從上面的源碼1中可以知道ThreadLocalMap是內(nèi)部數(shù)組Entry為弱引用(WeakReference)的Map蚊锹,且該引用為ThreadLocal<?>(弱引用為發(fā)生GC時清除對應(yīng)對象,該處設(shè)計的很巧妙稚瘾,認為線程執(zhí)行時間小于兩次gc間隔時間牡昆,則一個線程內(nèi)數(shù)據(jù)是能夠順序獲取的。)
2摊欠、從2中知道Entry初始大小為16丢烘,負載因子為2/3。
3些椒、在3中主要是設(shè)置線程在ThreadLocalMap對應(yīng)槽的值播瞳。
有這么一段代碼:

table[i] = new Entry(firstKey, firstValue);

因?qū)?yīng)槽中是線程的資源作為key,當線程銷毀時免糕,對應(yīng)的Entry[i]不會被銷毀狐史,而其對應(yīng)的線程已經(jīng)消失,此時需要清除這個Entry说墨,而因為采用了弱引用骏全,JVM會幫忙清除對應(yīng)Entry(這也是1中所說的設(shè)計巧妙的地方,避免產(chǎn)生內(nèi)存泄露)尼斧。
4姜贡、上面分析了Entry的實現(xiàn),那現(xiàn)在有這么一種情況棺棵,當線程為線程池中的線程時楼咳,我們知道線程池中的線程terminated的狀態(tài)后不會被銷毀熄捍,當新的請求到來時會作為其work線程繼續(xù)處理任務(wù),那此時母怜,如果該Entry的數(shù)據(jù)沒有被GC余耽,該ThreadLocal中存了上個請求的數(shù)據(jù),所以當在線程池中使用ThreadLocal時苹熏,記得清除數(shù)據(jù)碟贾。
二、ThreadLocal的實現(xiàn)
一中對ThreadLocal中的Map的實現(xiàn)進行了分析轨域,通過Map的槽存儲了以ThreadLocal為key的鍵值對袱耽,那是如何實現(xiàn)的呢?
在ThreadLocal中有兩個基本方法:
1)set方法

(1)
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
------------------------------------------------
(2)
 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
------------------------------------------------
(3)
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();
        }
------------------------------------------
(4)
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

1干发、(1)中g(shù)etMap((Thread)t)朱巨,第一次調(diào)用時,參考#1中Thread中ThreadLocals=nu l l枉长,第一次獲取map=null冀续,調(diào)用(4)new ThreadLocalMap(this, firstValue),見一(3)分析必峰。
2洪唐、后續(xù)set對應(yīng)線程值時,直接對ThreadLocalMap的槽操作自点。
3桐罕、replaceStaleEntry脉让、cleanSomeSlots這兩個方法是用來處理key為null時清除臟數(shù)據(jù)桂敛,暫時先不分析。

2)get方法

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return 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;
    }

get方法比較簡單溅潜,就是獲取桶中的對應(yīng)ThreadLocal的value术唬,如果為null就返回初始值(也是null,哈哈滚澜,點個贊吧)粗仓。
三、inheritableThreadLocals實現(xiàn)
前面講了ThreadLocal的實現(xiàn)设捐,同時也了解了Thread類中的ThreadLocals的作用和使用借浊。在此基礎(chǔ)上,來分析一下inheritableThreadLocals的實現(xiàn)萝招。
以下面源碼為引:

        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

上面是Thread中init的實現(xiàn)蚂斤,當父inheritableThreadLocals 存在的時候,也就是子線程可繼承時槐沼,調(diào)用ThreadLocal.createInheritedMap方法曙蒸,從下面的源碼中能夠看到捌治,子ThreadLocals初始化時,是對父inheritableThreadLocals的hash桶進行了完全復(fù)制:

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
-------------------------------------------------------
private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];
            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纽窟,一起剝皮案震驚了整個濱河市肖油,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌臂港,老刑警劉巖森枪,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異趋艘,居然都是意外死亡疲恢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門瓷胧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來显拳,“玉大人,你說我怎么就攤上這事搓萧≡邮” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵瘸洛,是天一觀的道長揍移。 經(jīng)常有香客問我,道長反肋,這世上最難降的妖魔是什么那伐? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮石蔗,結(jié)果婚禮上罕邀,老公的妹妹穿的比我還像新娘。我一直安慰自己养距,他們只是感情好诉探,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棍厌,像睡著了一般肾胯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耘纱,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天敬肚,我揣著相機與錄音,去河邊找鬼束析。 笑死艳馒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的畸陡。 我是一名探鬼主播鹰溜,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼虽填,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了曹动?” 一聲冷哼從身側(cè)響起斋日,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎墓陈,沒想到半個月后恶守,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡贡必,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年兔港,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仔拟。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡衫樊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出利花,到底是詐尸還是另有隱情科侈,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布炒事,位于F島的核電站臀栈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挠乳。R本人自食惡果不足惜权薯,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睡扬。 院中可真熱鬧盟蚣,春花似錦、人聲如沸威蕉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽韧涨。三九已至,卻和暖如春侮繁,著一層夾襖步出監(jiān)牢的瞬間虑粥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工宪哩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留娩贷,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓锁孟,卻偏偏與公主長得像彬祖,于是被迫代替她去往敵國和親茁瘦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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

  • 移步Android Handler機制詳解[http://www.reibang.com/p/e37e2db2b...
    凱玲之戀閱讀 822評論 0 0
  • 1. ThreadLocal的簡介 在多線程編程中通常解決線程安全的問題我們會利用synchronzed或者loc...
    先生zeng閱讀 704評論 2 8
  • 原創(chuàng)文章&經(jīng)驗總結(jié)&從校招到A廠一路陽光一路滄桑 詳情請戳www.codercc.com 1. ThreadLoc...
    你聽___閱讀 6,734評論 8 19
  • 湖邊柔柔的小風感動著我储笑,湖底各色的石頭感動著我甜熔,干涸土里掙扎怒放的花朵感動著我,忙碌的保潔員不停歇擦洗感動著我...
    echo_720a閱讀 196評論 1 1
  • 我煩死你了突倍。 別看我就是說你呢 就是現(xiàn)在正在看我這個簡述的人 我說的就是你 我沒有主動聯(lián)系你都不會主動聯(lián)系我 誰不...
    小快樂_ZJQ閱讀 216評論 1 2