Android 一起來(lái)看看 ThreadLocal

前言

說(shuō)起 ThreadLocal抛猖,大家可能會(huì)比較陌生政钟,但是如果想要比較好地理解 Android 的消息機(jī)制,ThreadLocal 是必須要掌握的樟结,這是因?yàn)?Looper 的工作原理养交,就跟 ThreadLocal 有很大的關(guān)系,理解 ThreadLocal 的實(shí)現(xiàn)方式有助于我們理解 Looper 的工作原理瓢宦,這篇文章就從 ThreadLocal 的用法講起碎连,一步一步帶大家理解 ThreadLocal。

一驮履、ThreadLocal 是什么


ThreadLocal 是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類(lèi)鱼辙,通過(guò)它可以在 指定的線程中 存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后玫镐,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù)倒戏,對(duì)于其他線程來(lái)說(shuō)則無(wú)法獲取到數(shù)據(jù)。

一般來(lái)說(shuō)恐似,當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時(shí)候杜跷,就可以考慮采用 ThreadLocal。

二矫夷、基本用法


創(chuàng)建葛闷,支持泛型

ThreadLocal<String> mStringThreadLocal = new ThreadLocal<>();

set 方法

mStringThreadLocal.set("developerHaoz");

get 方法

mStringThreadLocal.get();

接下來(lái)用一個(gè)完整的例子,幫助大家理解 ThreadLocal

    private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBooleanThreadLocal.set(true);
        Log.d(TAG, "Current Thread: mBooleanThrealLocal is : " + mBooleanThreadLocal.get());
        new Thread("Thread#1"){
            @Override
            public void run() {
                mBooleanThreadLocal.set(false);
                Log.d(TAG, "Thread 1: mBooleanThrealLocal is : " + mBooleanThreadLocal.get());

            }
        }.start();

        new Thread("Thread#2"){
            @Override
            public void run() {
                Log.d(TAG, "Thread 2: mBooleanThrealLocal is : " + mBooleanThreadLocal.get());

            }
        }.start();
    }

在上面的代碼中双藕,在主線程中設(shè)置 mBooleanThrealLocal 的值為 true淑趾,在子線程 1 中設(shè)置為 false,在子線程 2 中不設(shè)置 mBooleanThrealLocal 的值忧陪,然后分別在 3 個(gè)線程中通過(guò) get() 方法獲取 mBooleanThrealLocal 的值

image.png

從上面的日志中可以看出扣泊,雖然在不同的線程中訪問(wèn)的是同一個(gè) ThrealLocal 對(duì)象,但是它們通過(guò) ThrealLocal 獲取到的值確實(shí)不一樣的嘶摊,這就是 ThrealLocal 的奇妙之處了延蟹。

ThrealLocal 之所以有這么奇妙的效果,就是因?yàn)椴煌€程訪問(wèn)同一個(gè) ThrealLocal 的 get() 方法更卒,ThrealLocal 內(nèi)部都會(huì)從各自的線程中取出一個(gè)數(shù)組等孵,然后再?gòu)臄?shù)組中根據(jù)當(dāng)前 ThrealLocal 的索引去查找不同的 value 值稚照。

三蹂空、ThrealLocal 工作原理


ThrealLocal 是一個(gè)泛型類(lèi)俯萌,它的定義為 public class ThrealLocal<T>,只要弄清楚 ThrealLocal 的 set() 和 get() 方法就可以明白它的工作原理了上枕。

1咐熙、ThrealLocal 的 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);
    }

可以看到在 set() 方法中,先獲取到當(dāng)前線程辨萍,然后通過(guò) getMap(Thread t) 方法獲取一個(gè) ThreadLocalMap棋恼,如果這個(gè) map 不為空的話,就將 ThrealLocal 和 我們想存放的 value 設(shè)置進(jìn)去锈玉,不然的話就創(chuàng)建一個(gè) ThrealLocalMap 然后再進(jìn)行設(shè)置爪飘。

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

ThreadLocalMap 其實(shí)是 ThreadLocal 里面的靜態(tài)內(nèi)部類(lèi),而每一個(gè) Thread 都有一個(gè)對(duì)應(yīng)的 ThrealLocalMap拉背,因此獲取當(dāng)前線程的 ThrealLocal 數(shù)據(jù)就變得異常簡(jiǎn)單了师崎。

public class Thread implements Runnable {

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

下面看一下,ThrealLocal 的值到底是如何在 threadLocals 中進(jìn)行存儲(chǔ)的椅棺。在 threadLocals 內(nèi)部有一個(gè)數(shù)組犁罩,private Entry[] table,ThrealLocal 的值就存在這個(gè) table 數(shù)組中两疚。

    static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal> {
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
        private Entry[] table;
}

2床估、ThrealLocal 的 get() 方法

上面分析了 ThreadLocal 的 set() 方法,這里分析它的 get() 方法诱渤,代碼如下

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

可以發(fā)現(xiàn)丐巫,ThrealLocal 的 get() 方法的邏輯也比較清晰,它同樣是取出當(dāng)前線程的 threadLocals 對(duì)象勺美,如果這個(gè)對(duì)象為 null鞋吉,就調(diào)用 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;
    }

在 setInitialValue() 方法中,將 initialValue() 的值賦給我們想要的值励烦,默認(rèn)情況下谓着,initialValue() 的值為 null,當(dāng)然也可以重寫(xiě)這個(gè)方法坛掠。

    protected T initialValue() {
        return null;
    }

如果 threadLocals 對(duì)象不為 null 的話赊锚,那就取出它的 table 數(shù)組并找出 ThreadLocal 的 reference 對(duì)象在 table 數(shù)組中的位置。

從 ThreadLocal 的 set() 和 get() 方法可以看出屉栓,他們所操作的對(duì)象都是當(dāng)前線程的 threalLocals 對(duì)象的 table 數(shù)組舷蒲,因此在不同的線程中訪問(wèn)同一個(gè) ThreadLocal 的 set() 和 get() 方法,他們對(duì) ThreadLocal 所做的 讀 / 寫(xiě) 操作權(quán)限僅限于各自線程的內(nèi)部友多,這就是為什么可以在多個(gè)線程中互不干擾地存儲(chǔ)和修改數(shù)據(jù)牲平。

總結(jié)

ThreadLocal 是線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類(lèi),每個(gè)線程中都會(huì)保存一個(gè) ThreadLocal.ThreadLocalMap threadLocals = null;域滥,ThreadLocalMap 是 ThreadLocal 的靜態(tài)內(nèi)部類(lèi)纵柿,里面保存了一個(gè) private Entry[] table 數(shù)組蜈抓,這個(gè)數(shù)組就是用來(lái)保存 ThreadLocal 中的值。通過(guò)這種方式昂儒,就能讓我們?cè)诙鄠€(gè)線程中互不干擾地存儲(chǔ)和修改數(shù)據(jù)沟使。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渊跋,隨后出現(xiàn)的幾起案子腊嗡,更是在濱河造成了極大的恐慌,老刑警劉巖拾酝,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件燕少,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蒿囤,警方通過(guò)查閱死者的電腦和手機(jī)棺亭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蟋软,“玉大人镶摘,你說(shuō)我怎么就攤上這事≡朗兀” “怎么了凄敢?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)湿痢。 經(jīng)常有香客問(wèn)我涝缝,道長(zhǎng),這世上最難降的妖魔是什么譬重? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任拒逮,我火速辦了婚禮,結(jié)果婚禮上臀规,老公的妹妹穿的比我還像新娘滩援。我一直安慰自己,他們只是感情好塔嬉,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布玩徊。 她就那樣靜靜地躺著,像睡著了一般谨究。 火紅的嫁衣襯著肌膚如雪恩袱。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,696評(píng)論 1 312
  • 那天胶哲,我揣著相機(jī)與錄音畔塔,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛澈吨,可吹牛的內(nèi)容都是我干的把敢。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼棚辽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了冰肴?” 一聲冷哼從身側(cè)響起屈藐,我...
    開(kāi)封第一講書(shū)人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎熙尉,沒(méi)想到半個(gè)月后联逻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡检痰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年包归,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铅歼。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡公壤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出椎椰,到底是詐尸還是另有隱情厦幅,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布慨飘,位于F島的核電站确憨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瓤的。R本人自食惡果不足惜休弃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望圈膏。 院中可真熱鬧塔猾,春花似錦、人聲如沸稽坤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慎皱。三九已至老虫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茫多,已是汗流浹背祈匙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人夺欲。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓跪帝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親些阅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伞剑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

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