1. 程式人生 > >ThreadLocal實現原理

ThreadLocal實現原理

1.ThreadLocal可稱為執行緒區域性變數or本地執行緒變數,該類的作用是為每個執行緒都建立一個變數副本, 每個執行緒都可以修改自己所擁有的變數副本, 而不會影響其他執行緒的副本. 其實這也是解決執行緒安全的問題的一種方法。

2.變數副本是什麼時候“複製”到threadlocal中的呢?這裡“複製”兩個字用的很不專業。準確的說,應該是,變數副本【每個執行緒中儲存的那個map中的變數】是怎麼宣告和初始化的?從原始碼可以看出,當執行緒中的threadlocalmap是null的時候,會呼叫createmap建立一個map。同時根據函式引數設定上初始值。也就是說,當前執行緒的threadlocalmap是在第一次呼叫set的時候建立map並且設定上相應的值的。如果沒呼叫set之前直接呼叫get方法,也會初始化,呼叫的也是createmap方法。下面是set方法相關原始碼:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    /**
       * 獲取當前執行緒定義的ThreadLocal.ThreadLocalMap threadLocals = null;
     * @param t
     * @return
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
map等於null情況 begin
    
    /**
        * 建立ThreadLocalMap例項物件
     * @param t
     * @param firstValue
     */
    void createMap(Thread t, T firstValue) {
    	// this 指的是  ThreadLocal 例項變數
        t.threadLocals = new ThreadLocalMap(this, firstValue); 
    }
    
    /**
     * 
     * @param firstKey
     * @param firstValue
     */
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY]; // 陣列容量16
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
    
    private void setThreshold(int len) {
        threshold = len * 2 / 3;
    }
    
    static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        // 陣列是當前執行緒的成員變數ThreadLocal.ThreadLocalMap threadLocals 的成員變數,
        //用來儲存當前執行緒所有ThreadLocal變數的例項
        private Entry[] table; 
        private static final int INITIAL_CAPACITY = 16; // table 陣列初始化容量 
        private int size = 0; // 當前table存放的個數
        private int threshold; // Default to 0 // 何時擴容和此引數有關   len * 2 / 3;
        
map等於null情況 end    

map不等於null情況 begin
        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;
                }
             //出現k等於null的情況為,還沒有呼叫set的時候呼叫了get方法,
             //並將initialValue 初始化的值儲存到陣列中,詳情請看get方法
                if (k == null) { 
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value); // 該ThreadLocal物件之前並沒有儲存過
            int sz = ++size;
            // threshold=len * 2 / 3;
            // 符合條件開始擴容  擴容大小為當前陣列長度*2   
            if (!cleanSomeSlots(i, sz) && sz >= threshold) 
                rehash();
        }
        //獲取下一個下標
	    private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }
        
        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;
        }
        //開始擴容
        private void resize() {
            Entry[] oldTab = table;
            int oldLen = oldTab.length;
            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;
        }
map不等於null情況 end

3.每個執行緒的變數副本是儲存在哪裡的?

   可以從ThreadLocal的get方法的原始碼中看出來,其中getmap函式是用t作為引數,這裡t就是當前執行的執行緒。從而得知,get函式就是從當前執行緒的threadlocalmap中取出當前執行緒對應的變數的副本【注意,變數是儲存線上程中的,而不是儲存在ThreadLocal變數中】。當前執行緒中,有一個變數引用名字是threadLocals,這個引用是在ThreadLocal類中createmap函式內初始化的。每個執行緒都有一個這樣的threadLocals引用的ThreadLocalMap,以ThreadLocal和ThreadLocal物件宣告的變數型別作為引數。這樣,我們所使用的ThreadLocal變數的實際資料,通過get函式取值的時候,就是通過取出Thread中threadLocals引用的map,然後從這個map中根據當前threadLocal作為引數,取出資料。下面是get方法的原始碼:

    public T get() {
        Thread t = Thread.currentThread();
        //獲取當前執行緒的ThreadLocal.ThreadLocalMap threadLocals 例項物件
        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(); // 呼叫初始化方法
    }
    
    private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }
    
    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
        Entry[] tab = table;
        int len = tab.length;

        while (e != null) {
            ThreadLocal<?> k = e.get();
            if (k == key)
                return e;
            if (k == null)
                expungeStaleEntry(i);
            else
                i = nextIndex(i, len); // 獲取下標
            e = tab[i];
        }
        return null;
    }
    /**
      * 獲取initialValue初始化的值,並將初始化的值儲存到陣列中去
     * @return
     */
    private T setInitialValue() {
        // 呼叫初始化方法,可以在建立ThreadLocal例項的時候重寫該方法
        T value = initialValue(); 
        Thread t = Thread.currentThread();
        // 獲取當前執行緒成員變數 ThreadLocal.ThreadLocalMap threadLocals 的例項物件;
        ThreadLocalMap map = getMap(t); 
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value); // 建立 ThreadLocal.ThreadLocalMap threadLocals 例項物件
        return value;
    }