四滨达、ThreadLocal

ThreadLocal

定義

  • ThreaLocal提供了線程本地的實例。它與普通變量的區(qū)別在于倔撞,每個使用該變量的線程都會初始化一個==完全獨立==的實例副本

  • synchronize解決線程數(shù)據(jù)共享的帚屉,ThreadLocal提供了線程隔離

適用場景

適用于每個線程需要自己獨立的實例谜诫,且該實例需要在多個方法中被使用,也就是變量在線程間隔離而在方法或類間共享的場景

  • 例如攻旦,JAVA web中喻旷,我們需要用session獲取信息,有時候又需要修改session信息牢屋。
    • 這時候也可以用局部變量來解決問題且预,但是不好的地方是什么呢?
//在每個線程內(nèi)都構(gòu)建session實例
public class TheadLocalTest {

    class SessionLocal{
        String status;
    }

   public SessionLocal createSession(){
        return new SessionLocal();
    }
//需要顯式傳遞SessionLocal實例
   public String get(SessionLocal sessionLocal){
        return  sessionLocal.status;
    }
    //需要顯式傳遞SessionLocal實例
    void setStatus(SessionLocal sessionLocal,String status){
        sessionLocal.status=status;
    }

    public static void main(String[] args) {
        new Thread(() -> {
            TheadLocalTest theadLocalTest = new TheadLocalTest();
            SessionLocal session = theadLocalTest.createSession();
            theadLocalTest.get(session);
            System.out.println(session.status);
            theadLocalTest.setStatus(session, "close");
            theadLocalTest.get(session);
            System.out.println(session.status);
        }).start();
    }
}

也可以實現(xiàn)需求烙无,但是需要顯式傳遞session對象锋谐,方法間耦合度較高

使用ThreadLocal實現(xiàn)

public class TheadLocalTest {
//創(chuàng)建一個ThreadLocal對象
    public static ThreadLocal<SessionLocal> session = new ThreadLocal<SessionLocal>();

    class SessionLocal {
        String status;
    }
//創(chuàng)建一個SessionLocal
    public void createSession() {
         session.set(new SessionLocal());
    }
//直接通過ThreadLocal獲取,不用顯式傳遞SessionLocal實例
    public String get() {
        SessionLocal sessionLocal = session.get();
        return sessionLocal.status;
    }
//直接通過ThreadLocal設(shè)置皱炉,不用顯式傳遞SessionLocal實例
    void setStatus(String status) {
        session.get().status = status;
    }

    public static void main(String[] args) {
        new Thread(() -> {
            TheadLocalTest theadLocalTest = new TheadLocalTest();
            theadLocalTest.createSession();
            theadLocalTest.get();
            System.out.println(theadLocalTest.get());
            theadLocalTest.setStatus("close");
            theadLocalTest.get();
            System.out.println(theadLocalTest.get());
        }).start();
    }
}

實現(xiàn)原理

public class ThreadLocal<T> {
     ……
     //由靜態(tài)內(nèi)部類ThreadLocalMap提供Map
      static class ThreadLocalMap {
      //每個Entry都是一個對鍵的弱引用
          static class Entry  
                       extends WeakReference<ThreadLocal<?>> {
            Object value;
            //每個Entry都包含了一個對值的強引用
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
      }
}

1. 使用弱引用的原因:當沒有強引用指向ThreadLocal變量時怀估,它可以被回收。從而導致ThreadLocal不能被回收而造成內(nèi)存泄露合搅。
2. 另外一種內(nèi)存泄露:ThreadLocalMap維護ThreadLocal變量和具體映射多搀,當ThreadLocal被回收,映射的鍵就變?yōu)閚ull灾部。ThreadLocalMap中的Entry無法被移除康铭。從而使得實例被該Entry引用而無法被回收。
原理圖

從上面代碼也可以看出
Thread赌髓、ThreadLocal从藤、ThreadLocalMap、Entry之間的關(guān)系:

image
  • 一個Thread只有一個ThreadLocalMap
  • 一個ThreadLocalMap可以用多個ThreadLocal對象
  • 一個ThreadLocal對象對應一個Entry

內(nèi)存泄露

image
  1. 實線代表強引用锁蠕,虛線代表弱引用
  2. 虛線表示ThreadLocalMap是使用ThreadLocal的弱引用作為key的夷野。弱引用的對想在GC時會被回收
  3. 如果一個ThreadLocal沒有外部強引用(圖中的ref)來引用它,系統(tǒng)GC的時候荣倾,ThreadLocal就會被回收悯搔,就會出現(xiàn)很多key為null的Entry,如果線程不結(jié)束,value就會一直被引用舌仍,從而Entry就不能被回收妒貌,造成內(nèi)存泄漏
是弱引用導致的內(nèi)存泄露嗎?
  • 如果key使用強引用
    • 引用的ThreadLocal對象(ref)被回收了铸豁,但是ThreadLocalMap還持有ThreadLocal的強引用灌曙,那么ThreadLocal不會被回收,從而導致Entry內(nèi)存泄露
  • 弱引用
    • 不會出現(xiàn)上面的問題节芥。ThreadLocal會被回收在刺,value在下一次調(diào)用set、get、remove的時候也會清除蚣驼。
  • 所以忍燥,相比強引用,弱引用多一層保障
優(yōu)化
  • 在ThreadLocal的get(),set(),remover()的時候 都會清除線程ThreadLocalMap里所有key為null的value
  • 但是這些并不能保證不會內(nèi)存泄露
    • 使用static的ThreadLocal,延長了生命周期
    • 分配使用了ThreadLocal,但是沒有調(diào)用get隙姿、set、remover方法
  • 如何避免厂捞?
    • 使用完ThreadLocal,都調(diào)用remove()输玷,清除數(shù)據(jù)。
    • 如果ThreadLocal的生命周期需要和項目周期一樣靡馁,就不能用remove了欲鹏。

也可以參考這篇文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市臭墨,隨后出現(xiàn)的幾起案子赔嚎,更是在濱河造成了極大的恐慌,老刑警劉巖胧弛,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尤误,死亡現(xiàn)場離奇詭異,居然都是意外死亡结缚,警方通過查閱死者的電腦和手機损晤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來红竭,“玉大人尤勋,你說我怎么就攤上這事∫鹣埽” “怎么了最冰?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稀火。 經(jīng)常有香客問我暖哨,道長,這世上最難降的妖魔是什么憾股? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任鹿蜀,我火速辦了婚禮,結(jié)果婚禮上服球,老公的妹妹穿的比我還像新娘茴恰。我一直安慰自己,他們只是感情好斩熊,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布往枣。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪分冈。 梳的紋絲不亂的頭發(fā)上圾另,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音雕沉,去河邊找鬼集乔。 笑死,一個胖子當著我的面吹牛坡椒,可吹牛的內(nèi)容都是我干的扰路。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼倔叼,長吁一口氣:“原來是場噩夢啊……” “哼汗唱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丈攒,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤哩罪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后巡验,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體际插,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年显设,在試婚紗的時候發(fā)現(xiàn)自己被綠了腹鹉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡敷硅,死狀恐怖功咒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绞蹦,我是刑警寧澤力奋,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站幽七,受9級特大地震影響景殷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜澡屡,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一猿挚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驶鹉,春花似錦绩蜻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伊约。三九已至,卻和暖如春孕蝉,著一層夾襖步出監(jiān)牢的瞬間屡律,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工降淮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留超埋,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓佳鳖,卻偏偏與公主長得像纳本,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子腋颠,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

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