三. 線程管理之ThreadLocal

不忘初心 砥礪前行, Tomorrow Is Another Day !html

相關文章

本文概要:算法

  1. 認識ThreadLocal
  2. 瞭解ThreadLocal的實現原理

在Android系統源碼中,多處用到了Threadlocal,如最熟悉的Handler中的Looper,其次還有屬性動畫中AnimationHandler、ActivityThread、AMS都有涉及到.接下來一塊兒來認識與瞭解它.數組

一. 認識ThreadLocal

概念:線程內部的數據存儲類,能夠實如今不一樣線程具備不一樣數據副本.它的做用域僅限於當前當前線程,線程之間互不干擾.bash

基本使用

這裏定義了一個ThreadLocal用來存儲Integer類型的數據,設置了默認值爲999.分別在MainThread,SubThreadA對ThreadLocal的值進行設置,SubThreadB未進行設置值.多線程

示例源碼ide

public class ThreadLocalActivity extends AppCompatActivity {
    private static final String TAG = "ThreadLocalActivity";
    
    private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 999;
        }
    };

    public static void startActivity(Context pkgContext) {
        Intent intent = new Intent(pkgContext, ThreadLocalActivity.class);
        pkgContext.startActivity(intent);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_threadlocal);
        threadLocal.set(100);
        new SubThreadA("SubThreadA").start();
        new SubThreadB("SubThreadB").start();
        System.out.println("MainThread:" + threadLocal.get());

    }

    class SubThreadA extends Thread {

        public SubThreadA(String name) {
            super(name);
        }

        @Override
        public void run() {
            threadLocal.set(110);

            int value = threadLocal.get();

            System.out.println("SubThreadA:" + value);
        }
    }

    class SubThreadB extends Thread {

        public SubThreadB(String name) {
            super(name);
        }

        @Override
        public void run() {

            int value = threadLocal.get();

            System.out.println("SubThreadB:" + value);
        }
    }

}

//調用輸出
I: SubThreadA:110
I: MainThread:100
I: SubThreadB:999

複製代碼

最後經過調用輸出結果,能夠清晰的看到雖然操做的是同一個ThreadLocal對象,可是在三個不一樣線程存儲的值是互不影響的.回過頭再對照看概念性內容就清晰明瞭許多.另外在SubThreadB由於沒有設置值,因此得到的是咱們初始化設置的默認值999.oop

二. 瞭解ThreadLocal的實現原理

認識了它基本使用,接着一塊兒來了解ThreadLocal的基本原理,之因此用瞭解,由於筆者本身對ThreadLocal的理解有限且實際開發中運用較少.這裏就簡單的看下原理.post

2.1 當調用Set方法進行存儲值時.

對應源碼學習

public void set(T value) {
        Thread t = Thread.currentThread();
        //獲取當前線程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //不爲空,直接存儲值
            map.set(this, value);
        else
            //爲空,建立一個ThreadLocalMap並存儲值.
            createMap(t, value);

}
複製代碼

從上面源碼能夠看出,咱們設置的值,是存儲在ThreadLocalMap下的.當第一次存儲時,先會createMap初始化一個ThreadLocalMap.接着看如何進行初始化的.動畫

對應源碼

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

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            //將新值存儲在table數組中,該數組類型是Entry,擴展了弱引用封裝了ThreadLocal與value.
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
}
複製代碼

經過以上源碼,能夠很清楚的發現最終咱們設置的值,是存儲在一個Entry類型的table數組中的,這個Entry封裝了ThreadLocal與Value.

接着,若是不是第一次set值,那麼會進入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;
                }
            }
            //將值存儲在table數組中
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
複製代碼

這裏不分析具體算法,從上面能夠發現最終也是保存在table數組中.

2.2 當調用Get方法獲取存儲值時.

對應源碼

public T get() {
        Thread t = Thread.currentThread();
        //一樣獲取當前線程的ThreadLocalMap
        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();
}
複製代碼

經過以上源碼,get方法簡單來講就是從當前線程的ThreadLocalMap對象中獲取存儲的值.

經過SET與GET源碼簡單分析,咱們知道它們所操做的對象都是當前線程的ThreadLolocalMap對象的table數組,所以在不一樣線程中訪問同一個ThreadLocal的set和get方法,它們對ThreadLocal所作的讀寫操做僅限於各自線程的內部.

本文小結

最後咱們對ThreadLocal的實現流程作一個小結.

  1. Set方法時,將值存儲在當前線程的ThreadLolocalMap對象的table數組中.
    • 該table數組是Entry類型,封裝了ThreadLocal與Value.
  2. Get方法時,從當前線程的ThreadLolocalMap對象的table數組中獲取存儲的值.

最後奉上一個極簡版的ThreadLocal的工做流程圖.


ThreadLocal工做流程極簡圖

因爲本人技術有限,若有錯誤的地方,麻煩你們給我提出來,本人不勝感激,你們一塊兒學習進步.

參考連接:

相關文章
相關標籤/搜索