ThreadLocal詳解

前言

ThreadLocal類位于java.lang包下,jdk1.2開始引入。ThreadLocal為每個使用該類變量的線程提供單獨的副本励饵,線程之間不會互相影響。就是說在A線程設置的值只能在A線程讀取到滑燃,在B線程是讀取不到的役听。

在多線程環(huán)境下,每個線程都有自己的數(shù)據(jù)表窘。一個線程使用自己的局部變量比使用全局變量好典予,因為局部變量只有線程自己能看見,不會影響其他線程乐严,而全局變量的修改必須加鎖瘤袖。
但是局部變量也有問題,就是在函數(shù)調(diào)用的時候昂验,傳遞起來很麻煩捂敌。

基本使用

這個類一共提供了四個方法。

  • set(T value) 將此線程局部變量的當前線程副本中的值設置為指定值既琴。
  • get() 返回此線程局部變量的當前線程副本中的值占婉。
  • remove() 移除此線程局部變量當前線程的值。
  • initialValue() 返回此線程局部變量的當前線程的“初始值”甫恩。該實現(xiàn)返回 null逆济;如果程序員希望線程局部變量具有 null 以外的值,則必須為 ThreadLocal 創(chuàng)建子類磺箕,并重寫此方法奖慌。通常將使用匿名內(nèi)部類完成此操作。

下面看看ThreadLocal是如何解決這一問題的:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ThreadLocalDemo {

    private ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public void test() {
        threadLocal.set("hello");
        printValue(); //打印 main -> hello

        new Thread(new Runnable() {
            @Override
            public void run() {
                printValue(); //打印 Thread-0 -> null
            }
        }).start();
    }

    private void printValue() {
        System.out.println(Thread.currentThread().getName() + " -> " + threadLocal.get());
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadLocalDemo demo = new ThreadLocalDemo();
        demo.test();

        Thread.sleep(1000); //防止程序結(jié)束
    }
}

從上面代碼中可以看到不同線程獲取到的值是不一樣的松靡,在主線程設置的值只能在主線程獲取到简僧,在子線程返回了一個null。同時把ThreadLocal生明為全局變量击困,也不會影響各線程之間的值涎劈。

源碼分析

下面的代碼取自ThreadLocal類,展示了最基本的幾個方法阅茶。

//每個Thread都有自己的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

 public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    //在不同線程調(diào)用這個方法獲取到的ThreadLocalMap對象是不一樣的蛛枚,
    //所以從Map取到的對象更加不可能是一樣了。
    if (map != null)
        //用當前對象作為key存儲value
        map.set(this, value); 
    else
        createMap(t, value);
 }


 public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //用當前對象作為key獲取value
        //由于是用當前對象作為key脸哀,所以一個ThreadLocal對象只能存儲一個值蹦浦。
        //如果想要存儲幾個值可以多用幾個ThreadLocal。
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
    
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        //用當前對象作為key移除value
         m.remove(this);
}
    

上面的代碼可以很容易看出ThreadLocal的原理撞蜂,但是由于ThreadLocal的代碼太多盲镶,這只是很小的一部分,為了便于大家理解問題蝌诡,我寫了一個類模擬ThreadLocal的實現(xiàn)溉贿。

public class ThreadLocal<T> {

    //每個線程有單獨的Map來保證變量在不同線程之間互不影響
    private static final Map<Thread, Map<ThreadLocal, Object>> tMap = new ConcurrentHashMap<>();

    public void set(T value) {
        Thread thread = Thread.currentThread();
        Map<ThreadLocal, Object> map = tMap.get(thread);
        if (map != null) {
            map.put(this, value);
        } else {
            map = new ConcurrentHashMap<>();
            tMap.put(thread, map);
            map.put(this, value);
        }
    }

    public T get() {
        Thread thread = Thread.currentThread();
        Map<ThreadLocal, Object> map = tMap.get(thread);
        if (map != null) {
            return (T) map.get(this);
        }
        return null;
    }

    public void remove() {
        Thread thread = Thread.currentThread();
        Map<ThreadLocal, Object> map = tMap.get(thread);
        if (map != null) {
            map.remove(this);
        }
    }
}

總結(jié)

每個Thread都有自己的ThreadLocalMap,存儲value的時候會存儲到自己的ThreadLocalMap浦旱,所以在哪個線程設置的value就只能在哪個線程獲取到宇色,其他Thread是獲取不到的。因為ThreadLocal類用this當做key颁湖,所以每個ThreadLocal最多存儲一個value宣蠕。如果想要存儲幾個值可以多用幾個ThreadLocal

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甥捺,一起剝皮案震驚了整個濱河市抢蚀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌镰禾,老刑警劉巖皿曲,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吴侦,居然都是意外死亡谷饿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門妈倔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來博投,“玉大人,你說我怎么就攤上這事盯蝴∫慊” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵捧挺,是天一觀的道長虑绵。 經(jīng)常有香客問我,道長闽烙,這世上最難降的妖魔是什么翅睛? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任声搁,我火速辦了婚禮,結(jié)果婚禮上捕发,老公的妹妹穿的比我還像新娘疏旨。我一直安慰自己,他們只是感情好扎酷,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布檐涝。 她就那樣靜靜地躺著,像睡著了一般法挨。 火紅的嫁衣襯著肌膚如雪谁榜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天凡纳,我揣著相機與錄音窃植,去河邊找鬼。 笑死荐糜,一個胖子當著我的面吹牛撕瞧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狞尔,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丛版,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了偏序?” 一聲冷哼從身側(cè)響起页畦,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎研儒,沒想到半個月后豫缨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡端朵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年好芭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冲呢。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡舍败,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敬拓,到底是詐尸還是另有隱情邻薯,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布乘凸,位于F島的核電站厕诡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏营勤。R本人自食惡果不足惜灵嫌,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一壹罚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寿羞,春花似錦猖凛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽客年。三九已至霞幅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間量瓜,已是汗流浹背司恳。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绍傲,地道東北人扔傅。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像烫饼,于是被迫代替她去往敵國和親猎塞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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

  • 1. 概念 ThreadLocal 用于提供線程局部變量杠纵,在多線程環(huán)境可以保證各個線程里的變量獨立于其它線程里的變...
    zly394閱讀 1,745評論 0 1
  • ThreadLocal是一個關(guān)于創(chuàng)建線程局部變量的類荠耽。 通常情況下,我們創(chuàng)建的變量是可以被任何一個線程訪問并修改的...
    icecrea閱讀 722評論 0 2
  • Android Handler機制系列文章整體內(nèi)容如下: Android Handler機制1之ThreadAnd...
    隔壁老李頭閱讀 7,636評論 4 30
  • 前言 ThreadLocal很多同學都搞不懂是什么東西比藻,可以用來干嘛铝量。但面試時卻又經(jīng)常問到,所以這次我和大家一起學...
    liangzzz閱讀 12,452評論 14 228
  • ThreadLocal在java.lang包中银亲,其主要作用是提供一個和線程綁定的變量環(huán)境慢叨,即通過ThreadLoc...
    charming_coder閱讀 1,332評論 1 8