ThreadLocal筆記

ThreadLocal

背景

在android的消息隊列中钥顽,每個線程都可以創(chuàng)建一個Looper题画,那么在android中是如何管理Looper和線程的關(guān)系呢钦幔?在Looper.prepare()方法中可以看到窃诉,這里通過了一個ThreadLocal進(jìn)行每個線程中Looper的管理豆胸。

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

ThreadLocal是怎樣工作的奥洼,是怎樣保證不同線程中變量存取正確的,下面從源碼的角度看一看晚胡。

ThreadLocal

下面從源碼角度來看看ThreadLocal實現(xiàn)了什么樣的功能灵奖,從set()方法看起嚼沿,在set()方法中首先獲取了當(dāng)前調(diào)用set()方法的線程,然后通過這個線程獲取了一個ThreadLocalMap對象瓷患,如果存在就把當(dāng)前的ThreadLocal和需要儲存的value放到這個ThreadLocalMap對象中骡尽,如果不存在就新建一個ThreadLocalMap把當(dāng)前thread和value存進(jìn)去。代碼如下

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

這里看到creatMap()方法并沒有以當(dāng)前的ThreadLocal為key進(jìn)行存儲擅编,看看createMap()就不奇怪了攀细,在createMap()方法中還是通過當(dāng)前的ThreadLocal進(jìn)行存儲的,代碼如下

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

有set()就會有g(shù)et()爱态,下面來看看get()方法谭贪。首先還是先通過當(dāng)前線程thread對象獲取ThreadLocalMap,如果map存在再通過當(dāng)前ThreadLocal作為key進(jìn)行查找锦担,如果存在的話就返回對應(yīng)的值俭识,如果不存在則進(jìn)行一次對value的初始化,代碼如下

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

可以看到調(diào)用get()方法后洞渔,如果當(dāng)前ThreadLocal對應(yīng)的value不存在套媚,就會進(jìn)行一次初始化,在setInitialValue()中第一步就對value進(jìn)行了初始化磁椒,初始化的同時堤瘤,還是根據(jù)當(dāng)前線程去生成了一個ThreadLocalMap,并進(jìn)行和set()方法一樣的初始化浆熔,然后返回value本辐,代碼如下

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

在初始化value時調(diào)用了initialValue(),這里直接暴力的返回了null蘸拔,但是可以看到這個方法是protected的师郑,應(yīng)該是方便繼承時自己定義初始化值使用的环葵,如果直接重寫setInitialValue()是非常不安全的调窍。initialValue()代碼如下

protected T initialValue() {
    return null;
}

上述方法還會調(diào)到getMap()方法,這個方法也很簡單张遭,直接返回和當(dāng)前thread綁定的threadLocals邓萨,代碼如下。

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

以上就是ThreadLocal存取數(shù)據(jù)的主要代碼菊卷,上面一直提到ThreadLocalMap缔恳,下面來看看它究竟是什么。

ThreadLocalMap

ThreadLocalMap是ThreadLocal的一個靜態(tài)內(nèi)部類洁闰,從命名上猜測歉甚,它的作用應(yīng)該是類似于Java中的Map。首先在它的內(nèi)部定義了一個Entry類扑眉,定義了一個key-value格式的映射纸泄,key是ThreadLocal赖钞,value是任意類型,代碼如下

static class Entry extends WeakReference<ThreadLocal<?>> {
    
    Object value;
    
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

下面看看ThreadLocalMap中default的構(gòu)造方法聘裁,這個構(gòu)造方法只有在ThreadLocalMap第一次創(chuàng)建的時候調(diào)用雪营,即調(diào)用createMap()方法時調(diào)用,可以看到在構(gòu)造方法內(nèi)對Entry進(jìn)行了初始化衡便,其中的table是一個Entry數(shù)組献起,并傳出一個默認(rèn)為16的長度,通過ThreadLocal計算出一個hash值镣陕,并存在數(shù)組中下表的位置谴餐,這里和HashMap的實現(xiàn)原理類似,代碼如下茁彭。

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

在ThreadLocal中獲取entry時類似HashMap的取值方法总寒,通過Hash值計算出下標(biāo),從數(shù)組中取出對應(yīng)entry理肺,代碼如下

private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}

set()的時候也很簡單摄闸,如果key存在則替換value,不存在則通過replaceStaleEntry()去新增一個妹萨,代碼如下

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

小結(jié)

以上就是ThreadLocal中主要的幾個方法年枕,通過ThreadLocal和value進(jìn)行映射,對數(shù)據(jù)進(jìn)行存儲乎完,用起來十分簡單和方便熏兄,在處理多線程中變量儲存的時候可以起到很好的效果。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末树姨,一起剝皮案震驚了整個濱河市摩桶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帽揪,老刑警劉巖硝清,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異转晰,居然都是意外死亡芦拿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門查邢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔗崎,“玉大人,你說我怎么就攤上這事扰藕』嚎粒” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵邓深,是天一觀的道長未桥。 經(jīng)常有香客問我番官,道長,這世上最難降的妖魔是什么钢属? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任徘熔,我火速辦了婚禮,結(jié)果婚禮上淆党,老公的妹妹穿的比我還像新娘酷师。我一直安慰自己,他們只是感情好染乌,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布山孔。 她就那樣靜靜地躺著,像睡著了一般荷憋。 火紅的嫁衣襯著肌膚如雪台颠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天勒庄,我揣著相機(jī)與錄音串前,去河邊找鬼。 笑死实蔽,一個胖子當(dāng)著我的面吹牛荡碾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播局装,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坛吁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了铐尚?” 一聲冷哼從身側(cè)響起拨脉,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宣增,沒想到半個月后玫膀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡统舀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年匆骗,在試婚紗的時候發(fā)現(xiàn)自己被綠了劳景。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誉简。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盟广,靈堂內(nèi)的尸體忽然破棺而出闷串,到底是詐尸還是另有隱情,我是刑警寧澤筋量,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布烹吵,位于F島的核電站碉熄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肋拔。R本人自食惡果不足惜锈津,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凉蜂。 院中可真熱鬧琼梆,春花似錦、人聲如沸窿吩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纫雁。三九已至煌往,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間轧邪,已是汗流浹背刽脖。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留忌愚,地道東北人曾棕。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像菜循,于是被迫代替她去往敵國和親翘地。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

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