Handler---4部曲---2. ThreadLocal 存儲主流程

Handler---4部曲---1. 總體流程
Handler---4部曲---2. ThreadLocal 存儲主流程
Handler---4部曲---3. MessageQueue隊列
Handler---4部曲---4.細節補充

前言

看Handler源碼, 每次一說到ThreadLocal的保存, 幾乎都說可以理解成key就是當前線程, value就是對應的值. 實際上真的是這樣嗎?
主要了解整個存儲的流程,細節方面不做講解.

先上圖
一. 先記住

每個ThreadLocal對象中,threadLocalHashCode 的值是固定的

//ThreadLocal類中
final int threadLocalHashCode = nextHashCode();
注意:
  1. table數組的角標 i 和 ThreadLocal.threadLocalHashCode 有關.
  2. key當前ThreadLocal對象
  3. value 就是存入的值
二. ThreadLocal.set() 方法
    public void set(T value) {
        Thread t = Thread.currentThread();
        //從當前線程中, 獲取成員變量 threadLocals
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //保存數據
            map.set(this, value);
        else
            // 首次進入,創建一個ThreadLocalMap,並賦值給當前線程
            createMap(t, value);
    }

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

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

步驟:

  1. 從當前線程取出成員變量:ThreadLocalMap threadLocals;
  2. threadLocals爲空, 創建ThreadLocalMap, 並賦值.
  3. threadLocals不爲空,直接設置值
  4. 每個線程保存的本質, 就是在當前線程中的ThreadLocalMap對象中進行存儲.
三. 下面看看 new ThreadLocalMap() 和 ThreadLocalMap.set() 都做了些什麼?

3.1 ThreadLocalMap構造方法

        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            //初始化table數組, 數組裏面是沒有值的
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            //保存對應的值
            table[i] = new Entry(firstKey, firstValue);
        }

步驟:

  1. 初始化table數組---->沒有賦值
  2. tab[i] 保存值

3.2 ThreadLocalMap中的set方法

 private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            //tab的位置
            int i = key.threadLocalHashCode & (len-1);
            //Entry e = tab[i] --->當前 ThreadLocal 對應的值
            // 如果tab[i] 有值, 證明之前保存過
            for (Entry e = tab[i]; 
                    e != null; 
                    //這裏更新了i,  i = i+1
                    e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
                //同一個ThreadLocal, 直接覆蓋
                if (k == key) {
                    e.value = value;
                    return;
                }
            }
            //如果 tab[i] == null, 直接保存
            tab[i] = new Entry(key, value);
            int sz = ++size;
            //判斷table數組是否需要擴容
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

步驟:

  1. 查看tab[i]是否有值?
  2. 如果有值,並且key也相同, 覆蓋原來的值
  3. 如果沒有值,直接賦值.
  4. 判斷table數組 是否擴容.
結論:
1. 一個線程Thread,  只有一個ThreadLocalMap成員變量, 可以有多個ThreadLocal對象
2. 同一個線程,多個ThreadLocal時, 存儲的值在同一個Thread.ThreadLocalMap裏面的  table數組.  類型Entry[]
3. 不同線程, 同一個ThreadLocal時, 存儲在不同的Thread對象中.    1個Thread 對應 1個 ThreadLocalMap

到這裏整個保存的流程就結束了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章