ThreadLocal

在日常開發(fā)當(dāng)中,我們會(huì)將變量當(dāng)做參數(shù)一層層傳遞下去,但是突然有一天業(yè)務(wù)邏輯變了警医,在一個(gè)很深的地方需要加一個(gè)參數(shù),這樣你就要一層一層的往上加坯钦,確保這個(gè)參數(shù)能傳到調(diào)用的地方预皇。無疑,這是一件很糟糕的事情葫笼。而使用ThreadLocal可以解決這個(gè)問題

ThreadLocal的中文翻譯叫:線程局部變量深啤。

ThreadLocal的使用原因

他不是一個(gè)線程,而是一個(gè)線程的本地化對(duì)象路星。當(dāng)某個(gè)變量在使用ThreadLocal進(jìn)行維護(hù)時(shí)溯街,ThreadLocal為使用該變量的每個(gè)線程分配了一個(gè)獨(dú)立的變量副本,每個(gè)線程可以自行操作自己對(duì)應(yīng)的變量副本洋丐,而不會(huì)影響其他線程的變量副本呈昔。

通過ThreadLocal存取的數(shù)據(jù),總是與當(dāng)前線程相關(guān)友绝,也就是說堤尾,JVM 為每個(gè)運(yùn)行的線程,綁定了私有的本地實(shí)例存取空間迁客,從而為多線程環(huán)境常出現(xiàn)的并發(fā)訪問問題提供了一種隔離機(jī)制

ThreadLocal的實(shí)現(xiàn)

在ThreadLocal類中有一個(gè)Map郭宝,用于存儲(chǔ)每一個(gè)線程的變量的副本。ThreadLocal主要提供get()set()兩個(gè)方法掷漱。

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

ThreadLocalMap

ThreadLocalMapThreadLocal的一個(gè)靜態(tài)內(nèi)部類粘室,它實(shí)現(xiàn)了鍵值對(duì)的設(shè)置和獲取(類似于 Map<K,V> 存儲(chǔ)的key-value)卜范,每個(gè)線程中都有一個(gè)獨(dú)立的ThreadLocalMap副本衔统,它所存儲(chǔ)的值,只能被當(dāng)前線程讀取和修改海雪。ThreadLocal類通過操作每一個(gè)線程特有的 ThreadLocalMap 副本锦爵,從而實(shí)現(xiàn)了變量訪問在不同線程中實(shí)現(xiàn)隔離。因?yàn)槊總€(gè)線程的變量都是自己特有的奥裸,完全不會(huì)有并發(fā)錯(cuò)誤险掀。還有一點(diǎn)就是,ThreadLocalMap存儲(chǔ)的鍵值對(duì)中的鍵是this對(duì)象指向的ThreadLocal對(duì)象湾宙,而值就是你所設(shè)置的對(duì)象了

ThreadLocalMap

ThreadLocalMap的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)]) {
        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();
}

getMap和creatMap的實(shí)現(xiàn):

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

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

通過源碼分析可以看出迷郑,通過獲取和設(shè)置 Thread內(nèi)的threadLocals變量枝恋,而這個(gè)變量的類型就是 ThreadLocalMap,這樣進(jìn)一步驗(yàn)證了上文中的觀點(diǎn):每個(gè)線程都有自己獨(dú)立的ThreadLocalMap對(duì)象嗡害。打開java.lang.Thread類的源代碼,我們能得到更直觀的證明:

  /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

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

在獲取和當(dāng)前線程綁定的值時(shí)畦攘,ThreadLocalMap對(duì)象是以 this 指向的 ThreadLocal 對(duì)象為鍵進(jìn)行查找的霸妹,set() 方法是設(shè)置變量的拷貝副本,get() 方法通過鍵值對(duì)的方式獲取到這個(gè)本地變量的副本的value知押。

remove源碼:

    public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

該方法就是通過 this 找到ThreadLocalMap 中保存的變量副本做回收處理

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末叹螟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子台盯,更是在濱河造成了極大的恐慌罢绽,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件静盅,死亡現(xiàn)場(chǎng)離奇詭異良价,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蒿叠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門明垢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人市咽,你說我怎么就攤上這事痊银。” “怎么了施绎?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵溯革,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我谷醉,道長(zhǎng)致稀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任孤紧,我火速辦了婚禮豺裆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘号显。我一直安慰自己臭猜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布押蚤。 她就那樣靜靜地躺著蔑歌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揽碘。 梳的紋絲不亂的頭發(fā)上次屠,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天园匹,我揣著相機(jī)與錄音,去河邊找鬼劫灶。 笑死裸违,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的本昏。 我是一名探鬼主播供汛,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼涌穆!你這毒婦竟也來了怔昨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤宿稀,失蹤者是張志新(化名)和其女友劉穎趁舀,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祝沸,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矮烹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奋隶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擂送。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖唯欣,靈堂內(nèi)的尸體忽然破棺而出嘹吨,到底是詐尸還是另有隱情,我是刑警寧澤境氢,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布蟀拷,位于F島的核電站,受9級(jí)特大地震影響萍聊,放射性物質(zhì)發(fā)生泄漏问芬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一寿桨、第九天 我趴在偏房一處隱蔽的房頂上張望此衅。 院中可真熱鬧,春花似錦亭螟、人聲如沸挡鞍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽墨微。三九已至,卻和暖如春扁掸,著一層夾襖步出監(jiān)牢的瞬間翘县,已是汗流浹背最域。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锈麸,地道東北人镀脂。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像掐隐,于是被迫代替她去往敵國(guó)和親狗热。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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