ThreadLocal 源碼分析

寫(xiě)在前面

我們知道在使用handler進(jìn)行消息傳遞時(shí),需要?jiǎng)?chuàng)建Looper.prepare(),以及執(zhí)行Looper.loop()方法
不了解的可以看下Handler消息機(jī)制源碼分析。
查看Looper.java$prepare方法源碼

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

   private Looper(boolean quitAllowed) {
        //創(chuàng)建MessageQueue 這也是為什么一個(gè)Looper中只有一個(gè)MessageQueue的原因
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

可以看到是sThreadLocal調(diào)用了set方法創(chuàng)建Looper ,查看Looper.java代碼發(fā)現(xiàn)Looper是存儲(chǔ)在ThreadLocal里面的

  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal是什么呢 ,ThreadLocal是存儲(chǔ)當(dāng)前線程數(shù)據(jù)的數(shù)據(jù)存儲(chǔ)類(lèi)。
我們知道Looper.loop()方法中調(diào)用myLooper()獲取Looper對(duì)象,查看myLooper方法源碼

  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

發(fā)現(xiàn)是直接通過(guò)調(diào)用ThreadLocal類(lèi)中的get方法獲取存儲(chǔ)在ThreadLocal中的Looper。

源碼分析

這里先分析ThreadLocal的set方法查看其源碼

    public void set(T value) {
        //獲取當(dāng)前線程
        Thread t = Thread.currentThread();
        // ThreadLocalMap為T(mén)hreadLocal的靜態(tài)內(nèi)部類(lèi),后面會(huì)對(duì)該類(lèi)進(jìn)行分析
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

先看ThreadLocal類(lèi)的createMap方法

   void createMap(Thread t, T firstValue) {
        //t.threadLocals是一個(gè)ThreadLocalMap對(duì)象
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
//查看Thread類(lèi)
ThreadLocal.ThreadLocalMap threadLocals = null;

每個(gè)線程通過(guò)ThreadLocal.ThreadLocalMap與ThreadLocal進(jìn)行綁定,確保每個(gè)線程訪問(wèn)到的thread local variable都是該線程的。

現(xiàn)在分析ThreadLocal類(lèi)的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();
    }

先看setInitialValue()方法源碼

   private T setInitialValue() {
        //為thread local 初始化值
        T value = initialValue();
        //下面則與ThreadLocal的set方法源碼相同
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

從ThreadLocal的get方法中可以看到該方法調(diào)用了ThreadLocalMap中的set方法和getEntry方法,接下來(lái)分析ThreadLocalMap的實(shí)現(xiàn)。
我們先看ThreadLocalMap的介紹:是一個(gè)自定義的哈希映射表用于維護(hù)線程本地值。查看ThreadLocalMap的數(shù)據(jù)結(jié)構(gòu):

 //Entry 為T(mén)hreadLocalMap的靜態(tài)內(nèi)部類(lèi),繼承弱引用的ThreadLocal
 static class Entry extends WeakReference<ThreadLocal> {
           // 實(shí)際保存的對(duì)象值
            Object value;
            
            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

       //Entry[] 的初始大小,其值必須是2的n次冪
        private static final int INITIAL_CAPACITY = 16;

        //實(shí)際存放對(duì)象的容器,其大小必須是2的n次冪
        private Entry[] table;

        //Entry表元素的數(shù)量
        private int size = 0;

        //哈希表的擴(kuò)容閾值默認(rèn)值為0
        private int threshold; // Default to 0

查看ThreadLocalMap構(gòu)造函數(shù)

      ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            //用firstKey的threadLocalHashCode與初始值取模獲取元素放置的下標(biāo)位置
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            //初始化該節(jié)點(diǎn)
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            //設(shè)置閾值
            setThreshold(INITIAL_CAPACITY);
        }

firstKey 為T(mén)hreadLocal對(duì)象,ThreadLocalHashCode在被ThreadLocal創(chuàng)建的時(shí)候就生成了

 // 相當(dāng)于ThreadLocal的ID
 private final int threadLocalHashCode = nextHashCode();

接下來(lái)查看ThreadLocalMap類(lèi)中的getEntry方法源碼

   private Entry getEntry(ThreadLocal<?> key) {
            //根據(jù)key獲取索引
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            //如果對(duì)應(yīng)的Entry存在且未失效則返回
            if (e != null && e.get() == key)
                return e;
            else
                //使用線性探測(cè),繼續(xù)查找目標(biāo)Entry
                return getEntryAfterMiss(key, i, e);
        }

查看getEntryAfterMiss方法源碼

      private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;
            // 基于線性探測(cè)法不斷向后探測(cè)直到遇到空entry。
            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    //k為空則說(shuō)明Entry中對(duì)應(yīng)的ThreadLocal已經(jīng)被回收,調(diào)用該方法來(lái)清理無(wú)效的entry
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

查看expungeStaleEntry方法源碼

 // 該函數(shù)是ThreadLocal中的核心清理函數(shù),從staleSlot開(kāi)始遍歷
 //將對(duì)應(yīng)Entry中的Vaule值置為空
 private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // 將staleSlot對(duì)應(yīng)的Entry已經(jīng)Entry中的Value置為null
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            //從staleSlot開(kāi)始遍歷
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                // 清理對(duì)應(yīng)ThreadLocal已經(jīng)被回收的entry
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                         // 從計(jì)算出來(lái)的哈希位開(kāi)始往后查找,找到一個(gè)適合它的空位
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

查看ThreadLocalMap的set方法源碼

    private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            //線性探測(cè)
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
                //通過(guò)key查找到對(duì)應(yīng)的Entry
                if (k == key) {
                    e.value = value;
                    return;
                }
                //替換失效的Entry
                if (k == null) {
                    //如果entry里對(duì)應(yīng)的key為null的話,表明此entry為 舊的,就將其替換為當(dāng)前的key和value
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                //清除失效entry并進(jìn)行擴(kuò)容
                rehash();
        }

接著查看replaceStaleEntry方法源碼

 private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                       int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;
            Entry e;
            int slotToExpunge = staleSlot;
            //向前掃描,查找最前的一個(gè)無(wú)效slot
            for (int i = prevIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))
                if (e.get() == null)
                    slotToExpunge = i;

            //向后遍歷table
            for (int i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;

                    // Start expunge at preceding stale entry if it exists
                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                    // 清理無(wú)效slot
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                    return;
                }

                // 如果當(dāng)前的slot已經(jīng)無(wú)效,并且向前掃描過(guò)程中沒(méi)有無(wú)效slot,
                // 則更新slotToExpunge為當(dāng)前位置
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }

            // key沒(méi)找到就創(chuàng)建一個(gè)新的entry
            tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);

            // 如果運(yùn)行中有其他無(wú)效的slot則刪除它們對(duì)應(yīng)的entry
            if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }

查看cleanSomeSlots方法源碼

     private boolean cleanSomeSlots(int i, int n) {
            boolean removed = false;
            Entry[] tab = table;
            int len = tab.length;
            do {
                i = nextIndex(i, len);
                Entry e = tab[i];
                if (e != null && e.get() == null) {
                    n = len;
                    removed = true;
                    i = expungeStaleEntry(i);
                }
            } while ( (n >>>= 1) != 0);
            return removed;
        }

清除無(wú)效的slot發(fā)現(xiàn)最后還是調(diào)用的expungeStaleEntry方法。

查看ThreadLocalMap的rehash方法源碼

private void rehash() {
            //清除table中所有的失效的Entry
            expungeStaleEntries();

            // Use lower threshold for doubling to avoid hysteresis
            if (size >= threshold - threshold / 4)
                //對(duì)table的容量進(jìn)行2倍擴(kuò)容
                resize();
        }
//繼續(xù)查看
 private void resize() {
            Entry[] oldTab = table;
            int oldLen = oldTab.length;
            //以原來(lái)表容量的2倍進(jìn)行擴(kuò)容
            int newLen = oldLen * 2;
            Entry[] newTab = new Entry[newLen];
            int count = 0;

            for (int j = 0; j < oldLen; ++j) {
                Entry e = oldTab[j];
                if (e != null) {
                    ThreadLocal<?> k = e.get();
                    if (k == null) {
                        e.value = null; // Help the GC
                    } else {
                        int h = k.threadLocalHashCode & (newLen - 1);
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        newTab[h] = e;
                        count++;
                    }
                }
            }

            setThreshold(newLen);
            size = count;
            table = newTab;
        }

分析結(jié)束。

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

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

  • 首先通過(guò)問(wèn)題去看源碼 ThreadLocal通過(guò)空間換取線程變量安全的說(shuō)法正確嗎 ThreadLocal為什么說(shuō)會(huì)...
    Visualing閱讀 275評(píng)論 0 2
  • 在理解Handler、Looper之前,先來(lái)說(shuō)說(shuō)ThreadLocal這個(gè)類(lèi),聽(tīng)名字好像是一個(gè)本地線程的意思,實(shí)際...
    MrKing5946閱讀 280評(píng)論 0 1
  • 引言 ThreadLocal 可以在每個(gè)線程中存取數(shù)據(jù),并且不同的線程中的數(shù)據(jù)互不影響。使用在數(shù)據(jù)以線程為作用域并...
    任教主來(lái)也閱讀 193評(píng)論 0 0
  • ThreadLocal,線程變量,是一個(gè)以ThreadLocal對(duì)象為鍵,任意對(duì)象為值 的存儲(chǔ) 結(jié)構(gòu)。該結(jié)構(gòu)附著于...
    Justlearn閱讀 491評(píng)論 0 2
  • 概述 ThreadLocal是一種線程內(nèi)部存儲(chǔ)類(lèi)。通過(guò)他存儲(chǔ)的數(shù)據(jù)在不同的線程操作而互不干擾,類(lèi)似于各個(gè)線程中都有...
    皮球側(cè)飛閱讀 555評(píng)論 0 8

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