ThreadLocal 源碼解析

在activeJDBC框架內部的實現中看到了 ThreadLocal 這個類,記錄下了每一個線程獨有的鏈接java

private static final ThreadLocal<HashMap<String, Connection>> connectionsTL = new ThreadLocal<>();

感受是個知識點,就打開源碼看看了。先看一下源碼裏的解釋數組

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).框架

這個鳥文,瞎翻譯一下,就是:less

這個類提供了供線程專享的變量。這些變量不一樣與其它普通的變量,它是每一個線程都有一個本身的獨立初始化的變量(經過get和set方法實現)。這個類的實例經常使用於類的私有靜態字段,以實現每一個線程都有本身的狀態(例如userId,事務ID等)。ide

先跑一下用法吧,函數

package com.test.threadlocal;

public class TestController {
    
    private static int index = 0;
    private static String str = "這個字符串是每一個線程共享的";
    // 這個變量,看似是一個類的靜態屬性,實則是每一個線程有本身獨有的區域
    private static ThreadLocal<String> threadStr = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return "main線程專享";
        }
    };      
    
    public static void main(String[] args) throws InterruptedException {
        for(int i = 0; i < 3; i++) {
            Thread t = new MyThread();
            t.start();
            t.join();
        }
        
        System.out.println(str);
        System.out.println(threadStr.get());
    }
    
    static class MyThread extends Thread{

        @Override
        public void run() {
            index++;
            str = "第" + index + "個str";
            threadStr.set("第" + index + "個threadStr");
        }
        
        
    }

}

這個例子中,從str和threadStr變量的打印結果能夠看出來。str被全部的線程讀和寫,threadStr在每一個線程內部開闢了一塊線程專享的區域。接下來,咱們看一下具體實現。
先看一下構造函數this

/**
     * ThreadLocals rely on per-thread linear-probe hash maps attached
     * to each thread (Thread.threadLocals and
     * inheritableThreadLocals).  The ThreadLocal objects act as keys,
     * searched via threadLocalHashCode.  This is a custom hash code
     * (useful only within ThreadLocalMaps) that eliminates collisions
     * in the common case where consecutively constructed ThreadLocals
     * are used by the same threads, while remaining well-behaved in
     * less common cases.
     */
    private final int threadLocalHashCode = nextHashCode();
     /**
     * Creates a thread local variable.
     * @see #withInitial(java.util.function.Supplier)
     */
    public ThreadLocal() {
    }

構造函數是空的,可是,該類有一個私有整型常量threadLocalHashCodenextHashCode()方法咱們就不看了,省的一如源碼深似海。看鳥文的話,大概就是每new一個ThreadLocal變量的時候,就會生成一個散列碼,該碼非極端狀況下與某個整數取模後不容易衝突(這句話有點迷吧,其實我也不懂)
而後看一下set方法編碼

/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

容易看出,這個方法設置每一個線程本身的value,至關於當前線程是key,而後得出一個ThreadLocalMap。顯然,這個map用來保存線程內部的值,既然是map固然每一個線程能夠保存多個數值了,該map的value咱們猜一下就是我要保存的具體的值,估計是用Object類聲明的。那key是什麼呢?咱們看下ThreadLocalMap類的構造方法。線程

/**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;
                
/**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        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);
        }

個人天!這個類沒有繼承咱們想象中的HashMap,或者是ConcurrentMap,可是看過,Map的內部實現的同窗應該能夠發現,這個Map的實現和HashMap的實現簡直就是小巫見大巫,有沒有。它在構造函數中作了以下幾步:翻譯

  1. 初始化一個大小爲16的Entry數組
  2. 經過上面說過的很迷的HashCode散列值與15取模獲得將要存儲在數組中的索引值
  3. 構造Entry,而後保存進去
  4. 長度設置爲1
  5. 設置要擴容的限制大小爲16的2/3

咱們看到這個不就是用數組實現的Map嘛,看過HashMap實現的咱們,以爲灑灑水啦。
Map的set和get方法就不分析了。ThreadLocal的get方法咱們仍是要貼出來的,畢竟是咱們主要分析的東西

/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    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();
    }

可見,是獲取到當前線程,用做key獲取到Map,而後用當前this獲取到Entry實體。最後固然獲取到了存儲的value。

我編碼,我快樂~

本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索