ThreadLocal-從源碼看設計

ThreadLocal-從源碼看設計

一、ThreadLocal定義

ThreadLocal提供線程局部變量。這些變量與它們在每個線程中訪問,每個線程有自己的、獨立初始化的變量副本,實例通常是私有的類中希望將狀態與線程關聯的靜態字段。即在每個顯示訪問當前實例對象,都會獲取各自線程對應的值,不會相互影響,不存在線程安全問題,也不會影響程序執行的性能。由於在各自線程存儲對應的值,所以內存消耗會比直接使用實際類對象要大。

二、從源碼看設計

1.get方法

    /**
      *返回當前線程的副本中的值線程局部變量。 
      *如果該變量對於當前線程,它首先被初始化爲返回的值
      *通過調用{@link #initialValue}方法。
      *
      * @return 此本地線程的當前線程值
      **/
    public T get() {
        //獲取當前線程
        Thread t = Thread.currentThread();
        //獲取當前線程的ThreadLocalMap成員變量
        ThreadLocalMap map = getMap(t);
        //如果當前線程ThreadLocal已經初始化
        if (map != null) {
            //獲取當前ThreadLocal實例對應的Entry實體
            ThreadLocalMap.Entry e = map.getEntry(this);
            //如果不爲null,取出Entry對應值
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //如果當前線程ThreadLocal未初始化
        return setInitialValue();
    }

    /**
     * 獲取與ThreadLocal關聯的Map。 在InheritableThreadLocal中重寫。
     *
     * @param  t 當前線程
     * @return 當前線程的ThreadLocalMap
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

   /**
     * set()的變體來建立initialValue。如果用戶已覆蓋set()方法,請使用它代替set()。
     *
     * @return 初始化的值
     */
    private T setInitialValue() {
        //默認返回null
        T value = initialValue();
        //獲取當前線程
        Thread t = Thread.currentThread();
        //獲取線程對應map
        ThreadLocalMap map = getMap(t);
        //map已經實例化,則設置value返回;否則創建ThreadLocalMap
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }       

    /**
     * 創建ThreadLocal關聯的map對象,並賦值給當前線程。在InheritableThreadLocal中被重寫
     *
     * @param t 當前線程
     * @param firstValue 初始化map的實體值
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

2. set方法

   /**
     * 將此線程局部變量的當前線程副本設置爲指定值。 大多數子類將不需要重寫此方法,而僅依靠{@link #initialValue}方法來設置線程局部變量的值。
     *
     * @param value 要存儲在此本地線程的當前線程副本中的值。
     */
    public void set(T value) {
        //獲取當前線程
        Thread t = Thread.currentThread();
        //獲取當前線程對應的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //map不爲空,以當前對象爲key,值爲value設置進map;否則創建map
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

3.總結

get()方法

獲取的值是當前線程對象裏的ThreadLocalMap變量,然後從ThreadLocalMap變量中獲取到對應ThreadLocal對象的值。

set()方法

將要設置的值,保存到從當前線程獲取到的ThreadLocalMap中。

通過這兩個方法,就達到了多線程多個變量副本的效果。

4.ThreadLocalMap

是ThreadLocal的內部類,用於存儲不同ThreadLocal對象和對應的值。內部採用一個初始容量爲16的數組存儲ThreadLocal對應的Entry實體.


static class ThreadLocalMap {

        /**
         * 此哈希映射中的條目使用其主引用字段作爲鍵(始終是ThreadLocal對象)擴展WeakReference。 
         請注意,空鍵(即entry.get()== null)意味着不再引用該鍵,因此可以從表中刪除該條目。 
         
         在下面的代碼中,此類條目稱爲“陳舊條目”。
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** 這個ThreadLocal關聯的值 */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * 初始容量-必須是2的冪
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * 數組表,根據需要調整大小。 table.length必須始終爲2的冪。
         */
        private Entry[] table;

        /**
         * 表格中實體數量
         */
        private int size = 0;

        /**
         * 下一個要調整大小的大小值。
         */
        private int threshold; // 默認爲0
    }


擴容條件:當數組大小,超過容量的2/3擴容

    /**
      * 設置調整大小閾值以保持最壞的2/3負載係數。
      */
     private void setThreshold(int len) {
         threshold = len * 2 / 3;
     }

ThreadLocalMap在這裏只簡介,核心重點是分析同步機制,需要深入瞭解具體數據結構的,請查看源碼分析

ThreadLocal和同步機制區別

ThreadLocal:
1.空間換時間
2.在不同線程空間存儲多份實例

同步機制:
1.時間換空間
2.一個實例,不同線程排隊訪問

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