Java并發(fā)編程:ThreadLocal

我之前的一篇博客http://www.itdecent.cn/p/423544285f84,介紹了Android中的Service相關(guān)知識,其中有個點沒說,就是Service和IntentService的區(qū)別。接下來為了分析IntentService源碼,必須要先了解以下相關(guān)知識,HandlerThread、Handler、Looper、ThreadLocal等。今天這篇博客主要介紹ThreadLocal的使用以及實現(xiàn)原理。

ThreadLocal使用方法

在主線程中創(chuàng)建一個ThreadLocal對象,并通過set方法設(shè)置一個值,然后再創(chuàng)建一個子線程調(diào)用ThreadLocal對象set另外一個值,最后再創(chuàng)建一個子線程什么也不做,打印一下三個線程中的ThreadLocal對象get值。

ThreadLocal<Boolean> local = new ThreadLocal<>();
    local.set(true);
    Log.d(TAG,"main thread "+" threadlocal  is "+local.get());
    new Thread("Thread#1"){
      @Override
      public void run() {
        local.set(false);
        Log.d(TAG,getName()+" threadlocal  is "+local.get());
        super.run();
      }
    }.start();
    new Thread("Thread#2"){
      @Override
      public void run() {
        super.run();
        Log.d(TAG,getName()+" threadlocal  is "+local.get());
      }
    }.start();

輸出結(jié)果:
main thread  threadlocal  is true
Thread#1 threadlocal  is false
Thread#2 threadlocal  is null

從上面的日志中可以看出來,三個線程雖然都是調(diào)用同一個對象的get方法,但是結(jié)果卻完全不一樣,每個線程得到的值其實是他們所在線程的值,并不能讀取別的線程set的值。而且,當前線程set的值也不會影響到別的線程。非常神奇。

ThreadLocal源碼淺析

為了等下吃飯,寫作的動力立馬提高了不少,打字速度都提高了不少。

下面源碼是基于android-25分析的哦

上面的demo中總共就涉及到ThreadLocal的三個方法,一個構(gòu)造方法和set、get方法,構(gòu)造方法啥也沒有,來看set方法。
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);
    }

代碼很簡單哦,就是獲取調(diào)用改方法的線程,然后通過該線程獲取它的一個Map,如果Map為空,就createMap,不為空,就將需要設(shè)置的值設(shè)置到這個Map中了。

我們看一下createMap方法

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

從上面代碼可以發(fā)現(xiàn),這里為每個Thread都創(chuàng)建了一個成員變量threadLocals,這個threadLocals指向ThreadLocalMap,并且將ThreadLocal引用以及需要set的value值傳遞進去。

從這里應(yīng)該就可以看出來為什么可以做到多個線程可以不共享一個變量值了,因為每個線程都有一個自己的變量,那就是ThreadLocalMap,來存放自己的寶貝。

咱么調(diào)用ThreadLocal的set方法,其實就是調(diào)用ThreadLocal中的ThreadLocalMap這個內(nèi)部類的set方法,
我們接著看一下ThreadLocalMap,這個核心類哦。

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

上面方法的參數(shù),key就是ThreadLocal引用,value就是需要設(shè)置的值。在ThreadLocalMap中有一個數(shù)組,用來存放<ThreadLocal,value>這個鍵值對,存放的位置就是通過int i = key.threadLocalHashCode & (len-1);來計算的,也就是根據(jù)ThreadLocal引用來計算在數(shù)組中的位置

考慮一下,這里為什么要弄了數(shù)組存放鍵值對?主要是因為一個線程可能有多個ThreadLocal對象了。

再來看一下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();
    }

很簡單啊,首先獲取到當前線程的成員變量ThreadLocalMap,然后根據(jù)ThreadLocal引用來獲取數(shù)組中存放的鍵值對,也就能獲取到當前線程之前保存的value值了。

關(guān)于這個ThreadLocal到底有什么作用,下回分解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容