ThreadLocal到底是個啥?

ThreadLocal是什么

提供線程局部變量囊榜,一個線程的局部變量在多個線程中有獨立的副本审胸,特點有:簡單(開箱即用),快速(無額外開銷)卸勺,安全(線程安全)砂沛;場景:多線程場景(資源持有,并發(fā)計算曙求,線程一致性碍庵,線程安全)使用hash表實現(xiàn),幾乎所有提供多線程特征的語言都是其應(yīng)用范圍悟狱。

ThreadLocal基本的API:帶有泛型的構(gòu)造函數(shù)静浴,訪問器get/set,初始化,回收挤渐。

ThreadLocal原理

ThreadLocal苹享,連接ThreadLocalMap和Thread。來處理Thread的TheadLocalMap屬性浴麻,包括init初始化屬性賦值得问、get對應(yīng)的變量,set設(shè)置變量等软免。通過當(dāng)前線程宫纬,獲取線程上的ThreadLocalMap屬性,對數(shù)據(jù)進行g(shù)et膏萧、set等操作漓骚。ThreadLocalMap蝌衔,用來存儲數(shù)據(jù),采用類似hashmap機制蝌蹂,存儲了以threadLocal為key噩斟,需要隔離的數(shù)據(jù)為value的Entry鍵值對數(shù)組結(jié)構(gòu)。ThreadLocal叉信,有個ThreadLocalMap類型的屬性亩冬,存儲的數(shù)據(jù)就放在這兒。

ThreadLocalMap是ThreadLocal內(nèi)部類硼身,由ThreadLocal創(chuàng)建,Thread有ThreadLocal.ThreadLocalMap類型的屬性覆享。

源碼盤點

ThreadLocal源碼目錄
public void set(T value) {
      Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);
         if (map != null)
           map.set(this, value);
         else
           createMap(t, value);
    }

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

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

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

ThreadLocalMap

這個類在構(gòu)造中創(chuàng)建了一個數(shù)組佳遂, new Entry[INITIAL_CAPACITY]; ,Entry里面就是一個object的對象,然后里面主要getEntry和set方法進行存取和讀取。

    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)]) {
            if (e.refersTo(key)) {
                e.value = value;
                return;
            }

            if (e.refersTo(null)) {
                replaceStaleEntry(key, value, i);  // 刪除key為null的Entry
                return;
            }
        }

        tab[i] = new Entry(key, value);
        int sz = ++size;
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
    }

    private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);//根據(jù)key獲取位下標(biāo)
        Entry e = table[i];  // 根據(jù)下標(biāo)撒顿,獲取這個Entry 里面是一個object丑罪,實現(xiàn)了軟引用
        if (e != null && e.get() == key)
            return e; //校驗沒問題后返回
        else
            return getEntryAfterMiss(key, i, e);
    }

    private void remove(ThreadLocal<?> key) {
        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)]) {
            if (e.refersTo(key)) {
                e.clear();
                expungeStaleEntry(i);
                return;
            }
        }
    }

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

        while (e != null) {
            if (e.refersTo(key))
                return e;
            if (e.refersTo(null))          // 刪除key為null的Entry
                expungeStaleEntry(i);
            else
                i = nextIndex(i, len);
            e = tab[i];
        }
        return null;
    }

使用場景

四種核心場景:

1、線程資源持有(ThreadLocalMap實現(xiàn)凤壁,持有線程資源供線程的各個部分使用吩屹,全局獲取減少變成難度)

2、線程資源一致性(幫助需要保持線程一致的資源拧抖,維護一致性煤搜,降低編程難度,例如:JDBC會話連接)

3唧席、線程安全(幫助只考慮了單線程的程序庫擦盾,無遐想多線程場景遷移)

4、分布式計算(幫助分布式計算場景的各個線程累計局部計算結(jié)果)

ThreadLocal內(nèi)存泄漏淌哟,如何避免

內(nèi)存泄漏為程序在申請內(nèi)存后迹卢,無法釋放已申請的內(nèi)存空間,一次泄露危害可以忽略徒仓,但內(nèi)存泄漏堆積后果很嚴(yán)重腐碱,無論多少內(nèi)存,遲早會被占光掉弛。

不再會被使用的對象或者變量占用的內(nèi)存空間不能被回收症见,就是內(nèi)存泄漏。

強引用:使用最普遍的一個引用(new)狰晚,一個對象具有強引用筒饰,不會被垃圾收集器回收。當(dāng)內(nèi)存空間不足壁晒,java虛擬機寧愿OOM瓷们,都不會回收。

如果想取消強引用和某個對象之間的關(guān)聯(lián),可以顯示將對象復(fù)制為null谬晕,這樣jvm就會在安全區(qū)域執(zhí)行g(shù)c進行垃圾回收碘裕。

弱引用:jvm進行垃圾回收時,無論內(nèi)存是否充足攒钳,都會回收被弱引用關(guān)聯(lián)的對象帮孔。在java中,用WeakReference類來表示不撑。

ThreadLocal的實現(xiàn)原理文兢,每一個Thread維護一個ThreadLocalMap,ThreadLocalMap是由一個個Entry構(gòu)成焕檬,而Entry繼承了弱引用姆坚,key為使用弱引用的ThreadLocal對象,value為線程變量的副本实愚。

ThreadLocalMap使用ThreadLocal的弱引用作為key兼呵,如果一個ThreadLocal不存在外部的強引用時,Key(ThreadLocal對象)勢必會被GC回收腊敲,這樣就會導(dǎo)致ThreadLocalMap中的key為null击喂,而value還存在著強引用,只有thread線程退出以后碰辅,value的強引用鏈才會斷掉懂昂,但是如果線程遲遲不結(jié)速的話,這些key為null的Entry的value就會一直存在引用鏈乎赴。

key使用強引用
當(dāng)ThreadLocalMap的key使用強引用時忍法,此時若是外部的ThreadLocal對象被置為null,按理說應(yīng)該被回收榕吼,但是ThreadLocalMap中還持有對ThreadLocal的強引用饿序,如果沒有手動刪除,那么ThreadLocal不會被回收羹蚣,導(dǎo)致Entry內(nèi)存泄漏原探。

key使用弱引用
當(dāng)ThreadLocalMapkey為弱引用回收ThreadLocal對象時,由于ThreadLocalMap只持有ThreadLocal的弱引用顽素,即使沒有手動刪除咽弦,也不會影響ThreadLocal的回收。當(dāng)key為null時胁出,在下一個調(diào)用ThreadLocalMap的set型型、getremove方法時會清除value值全蝶。

ThreadLocal正確使用方法:

  • 每次使用完ThreadLocal都調(diào)用它的remove方法清楚數(shù)據(jù)
  • 將ThreadLocal變量定義為private static闹蒜,這樣就一直存在ThreadLocal的強引用寺枉,也就能保證在任何時候都能通過ThreadLocal的弱引用訪問到Entry的value值,繼續(xù)清除绷落。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姥闪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子砌烁,更是在濱河造成了極大的恐慌筐喳,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件函喉,死亡現(xiàn)場離奇詭異避归,居然都是意外死亡,警方通過查閱死者的電腦和手機函似,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門槐脏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撇寞,你說我怎么就攤上這事√寐龋” “怎么了蔑担?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咽白。 經(jīng)常有香客問我啤握,道長,這世上最難降的妖魔是什么晶框? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任排抬,我火速辦了婚禮,結(jié)果婚禮上授段,老公的妹妹穿的比我還像新娘蹲蒲。我一直安慰自己,他們只是感情好侵贵,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布届搁。 她就那樣靜靜地躺著,像睡著了一般窍育。 火紅的嫁衣襯著肌膚如雪卡睦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天漱抓,我揣著相機與錄音表锻,去河邊找鬼。 笑死乞娄,一個胖子當(dāng)著我的面吹牛瞬逊,可吹牛的內(nèi)容都是我干的显歧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼码耐,長吁一口氣:“原來是場噩夢啊……” “哼追迟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起骚腥,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤敦间,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后束铭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廓块,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年契沫,在試婚紗的時候發(fā)現(xiàn)自己被綠了带猴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡懈万,死狀恐怖拴清,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情会通,我是刑警寧澤口予,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站涕侈,受9級特大地震影響沪停,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裳涛,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一木张、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧端三,春花似錦舷礼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虚婿,卻和暖如春旋奢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背然痊。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工至朗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剧浸。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓锹引,卻偏偏與公主長得像矗钟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嫌变,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

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