ThreadLocal源代碼3

public class ThreadLocal1<T> {
    //當建立了一個 ThreadLocal 的實例後,它的散列值就已經肯定了//threadLocal實例的hashCode是經過nextHashCode()方法實現的,該方法實際上老是用一個AtomicInteger(初始值爲0)加上0x61c88647來實現的。
    //0x61c88647這個數是有特殊意義的,它可以保證hash表的每一個散列桶可以均勻的分佈,這是Fibonacci Hashing,
    //0x61c88647 這個就比較神奇了,它可使 hashcode 均勻的分佈在大小爲 2 的 N 次方的數組裏。下面寫個程序測試一下:
    //也正是可以均勻分佈,因此threadLocal選擇使用開放地址法來解決hash衝突的問題。
    /*public static void main(String[] args) {
        AtomicInteger hashCode = new AtomicInteger();  // 一直在增長
        int hash_increment = 0x61c88647;
        int size = 16;
        List <Integer> list = new ArrayList <> ();
        for (int i = 0; i < size; i++) {
            list.add(hashCode.getAndAdd(hash_increment) & (size - 1));
        }
        System.out.println("original:" + list);
        Collections.sort(list);
        System.out.println("sort:    " + list);
    }
    size=16:   [7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9, 0]       [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
    size=32  [7, 14, 21, 28, 3, 10, 17, 24, 31, 6, 13, 20, 27, 2, 9, 16, 23, 30, 5, 12, 19, 26, 1, 8, 15, 22, 29, 4, 11, 18, 25, 0]
             [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
    */
    private final int threadLocalHashCode = nextHashCode();//對象的屬性,不變化的。
    private final static int HASH_INCREMENT = 0x61c88647;//1640531527。類的屬性。 //類的屬性,對象共享,一直在增長。
    private static AtomicInteger nextHashCode = new AtomicInteger();//0

    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);//nextHashCode本身變成了HASH_INCREMENT=1640531527
    }

    protected T initialValue() {//用於重寫
        return null;
    }

    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

    public ThreadLocal() {
    }

    //有可能第一次調用get不調用set,map爲null,setInitialValue-initialValue賦予map=threadLocals初值。
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//返回調用get()的線程的threadLocals,是一個ThreadLocalMap,這個ThreadLocalMap是ThreadLocal的內部類。
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();//若是map爲空,也就是第一次沒有調用set直接get(或者調用過set,又調用了remove)時,爲其設定初始值
    }

    private T setInitialValue() {
        T value = initialValue();//initialValue方法爲第一次調用get方法提供一個初始值。
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
    /*  ThreadLocal<Integer> x = new ThreadLocal<Integer>();
     for (int i = 0; i < 3; i++) {
        new Thread(new Runnable() {
            public void run() {
                x.set(new Random().nextInt()); 
            }
        }).start();
    }*/
    //A,B,C線程經過共享變量ThreadLocal<Integer> x.set(s)。A,B,C線程分別修改的是本身線程的threadLocals=ThreadLocalMap
    // 別的線程修改不了這個線程的threadLocals,因此對這個線程的ThreadLocalMap修改時候沒有線程安全問題。
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;//返回這個線程的threadLocal屬性
    }
void createMap(Thread t, T firstValue) {//threadLocals是一個ThreadLocalMap,key是調用ThreadLocal方法的這個ThreadLocal。 //ThreadLocal的方法確定是ThreadLocal對象在調用。 t.threadLocals = new ThreadLocalMap(this, firstValue); }
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
T childValue(T parentValue) {
throw new UnsupportedOperationException(); } static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { private final Supplier<? extends T> supplier; SuppliedThreadLocal(Supplier<? extends T> supplier) { this.supplier = Objects.requireNonNull(supplier); } @Override protected T initialValue() { return supplier.get(); } } static class ThreadLocalMap1 {//ThreadLocalMap裏面有一個數組table,table裏面的元素是Entry extends WeakReference。 //WeakReference裏面屬性有value和referent=ThreadLocal。ThreadLocal是被弱引用關聯。 //ThreadLocal由強引用變成了弱引用,由於Entry繼承了WeakReference,在Entry的構造方法中,調用了super(k)方法就會將threadLocal實例包裝成一個WeakReferenece static class Entry extends WeakReference<ThreadLocal<?>> { Object value;//value是真正須要存儲的Object//ThreadLocalMap的Entry自己就是虛引用, //WeakReference<B> weakReference = new WeakReference<B>(b1, rq); b1=null以後,weakReference就回去排隊。 //Entry繼承WeakReference,ThreadLocal=k釋放了整個Entry就會去排隊 Entry(ThreadLocal<?> k, Object v) {//ThreadLocal放在WeakReference裏面。 super(k);//ThreadLocal=null被gc後該 Entry 就會進入到 ReferenceQueue 中 value = v; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table; private int size = 0;//實際個數 private int threshold; private void setThreshold(int len) {//加載因子爲2/3,因此哈希表可用大小爲:16*2/3=10,即哈希表可用容量爲10。 threshold = len * 2 / 3;//threshold是總共容量的2/3。 } private static int nextIndex(int i, int len) {//i+1對len取餘 return ((i + 1 < len) ? i + 1 : 0); } private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1);//循環 } //ThreadLocalMap是線程的屬性,key是一個個的ThreadLocal,value是值。 ThreadLocalMap1(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);//threshold是總共容量的2/3。 } private ThreadLocalMap1(ThreadLocalMap1 parentMap) { Entry[] parentTable = parentMap.table;//獲取table int len = parentTable.length;//設置容量大小 setThreshold(len);//設置threshold table = new Entry[len]; for (int j = 0; j < len; j++) { Entry e = parentTable[j];//獲取每個Entry if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();//獲取key是ThreadLocal if (key != null) { Object value = key.childValue(e.value);//e.value獲取的是value Entry c = new Entry(key, value);//構造新的Entry int h = key.threadLocalHashCode & (len - 1);//計算table的索引 while (table[h] != null) h = nextIndex(h, len);//索引加一從新計算索引 table[h] = c;//放入table size++; } } } } 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// hash衝突 return getEntryAfterMiss(key, i, e); } //ThreadLocalMap 中採用開放定址法,因此當前 key 的散列值和元素在數組中的索引並不必定徹底對應。 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) {//e是i位置的值,爲null直接退出 ,null後面有key相等也無論了null爲邊界。因此get(61)就get不到了 ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null)//get時候遇到髒數據擦除 。由於強引用的ThreadLocal在外部置位了空。擦除這個位置時候,還會向右以null爲邊,擦除其餘髒數據。 expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; } //set也會擦除髒數據。設置時候不必定是新增,有多是以前已經有了去替換 private void set(ThreadLocal<?> key, Object value) { Entry[] tab = table; int len = tab.length; //哈希表大小老是爲2的冪次方,因此相與等同於一個取模的過程, int i = key.threadLocalHashCode & (len-1); //若是不考慮remove,全部跟i相關的都在i一塊兒,而且是以null爲邊界的。考慮remove,全部跟i同樣的是在一塊兒而且null爲邊界,可是中間有可能有null//e=null退出說明沒有衝突,不爲null說明衝突了。 線性探測。 向右移動,null爲邊界 for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {//環形查找,不會死循環,由於會擴容。死循環是正常數據佔據了所有位置。 ThreadLocal<?> k = e.get(); if (k == key) {//相等就覆蓋 e.value = value; return; } //放到髒數據位置,髒數據後面有key相等的也無論了。放髒數據位置時候會向右一直找到null看有沒有key相等的null後面有key相等的也無論了。最後放到髒數據位置 if (k == null) { replaceStaleEntry(key, value, i); return; } } //i個位置e=null,直接放 tab[i] = new Entry(key, value); int sz = ++size; //從i位置開始清除,清除掉了就不用hash,沒有清除掉就hash。 if (!cleanSomeSlots(i, sz) && sz >= threshold)//size > len * 2 / 3 rehash(); } private void remove(ThreadLocal<?> key) { 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)]) {//null後面有key相等的也不清除了。 if (e.get() == key) {//是根據ThreadLocal=key來判斷是否同一個。 e.clear();//置WeakReference裏面強引用referent=ThreadLocal=null,WeakReference裏面清除引用關聯只能clear()。由於是私有的。 expungeStaleEntry(i);//清除i位置,並向右以null爲界清除其餘髒數據 return; } } } //替換到髒數據staleSlot位置:也不是放到髒數據位置,而是從staleSlot開始向右一直到null先找一遍,有key相等就交換 並放到staleSlot並清除這個位置,不然放到髒數據staleSlot位置//replaceStaleEntry並不只僅侷限於處理當前已知的髒entry,它認爲在出現髒entry的相鄰位置也有很大機率出現髒entry,因此爲了一次處理到位,就須要向前環形搜索,找到前面的髒entry private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) { Entry[] tab = table; int len = tab.length;//總長度,不是實際長度。 Entry e; int slotToExpunge = staleSlot; //slotToExpunge是準備要清理的位置。 //slotToExpunge=null爲邊界左邊最遠髒數據位置。e都不爲null就是死循環。因此確定有null的。null在最後幾個。remove也會有null。 //循環都是以null做爲邊界的。null後面有key相等也不要了 for (int i = prevIndex(staleSlot, len);/*往前移動一個位置,會又走回來 */ (e = tab[i]) != null; i = prevIndex(i, len)) if (e.get() == null) slotToExpunge = i; // 清除數據也是以null做爲邊界的。 // 向右找,null就退出,中間有key相等的就中止。都不爲null也沒有相等的就死循環。 for (int i = nextIndex(staleSlot, len);/*往右移動一個位置,會又走回來 */ (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == key) {//向右移動,找到key相等的就去覆蓋。 key跟i位置相等 e.value = value; tab[i] = tab[staleSlot];//staleSlot是髒數據位置。i位置是key同樣的位置。 tab[staleSlot] = e;//e仍是放在了staleSlot位置。i這個key相等的位置直接被髒數據覆蓋了。 //slotToExpunge == staleSlot不可能向左找又回來了, 找回來就是所有不是null,就是死循環。 if (slotToExpunge == staleSlot)//向左沒有找到髒數據。第一個for循環沒有改變slotToExpunge的值。 slotToExpunge = i;//清除的位置爲i,不然清除位置是左邊的髒數據。確定要以左邊的清除位置爲準,由於能夠清除的更多 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);//expungeStaleEntry(slotToExpunge)清除slotToExpunge位置, //slotToExpunge到返回值位置都已經沒有髒數據,cleanSomeSlots(expungeStaleEntry(slotToExpunge), len)是從返回值位置開始清除髒數據 return; } if (k == null && slotToExpunge == staleSlot)//slotToExpunge == staleSlot說明向左找又回來了, 說明e都不爲null,會是死循環。 slotToExpunge = i;//左邊沒有髒數據右邊i是髒數據。第一個for循環沒有改變slotToExpunge的值。 清除的位置變爲i。不然清除位置是左邊的髒數據。確定要以左邊的清除位置爲準,由於能夠清除的更多 } //像右走,一直到e=null位置,都沒有找到key相等的位置。就放到staleSlot髒數據位置 tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); // staleSlot是放數據的位置,不能清除。 if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); } //清除i位置,而且i向右一直到null清除或者從新找位置 private int expungeStaleEntry(int staleSlot) { Entry[] tab = table;//局部變量是爲了加強引用,不回收。 int len = tab.length; // staleSlot位置 置位null。 tab[staleSlot].value = null;//Entry裏面的value解除關係,Entry裏面的referent已經解除關係了。 tab[staleSlot] = null;//Entry斷掉引用關係, size--; // staleSlot位置移除可,日後一直在null,Rehash Entry e; int i; //上面把staleSlot位置置爲null了。從staleSlot向右一個個看,key爲null就清除,不爲null就從新算位置。e=null退出循環。 for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get();//get()方法返回的是referent=ThreadLocal。 if (k == null) {//e不爲null key是null就移除, e.value = null; tab[i] = null;//value移除,entyr解除關係,ThreadLocal已經解除關係爲null了。 size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) {//從新找位置h tab[i] = null; while (tab[h] != null) h = nextIndex(h, len);//h有元素,從新找h tab[h] = e;//放到h的位置上去 } } } return i;//舊i和新i之間所有沒有髒數據了新i是null的位置。 } //原本只准備i開始移動log2(n)次,移動過程當中發現有髒數據,就改變移動次數再次移動log2(len)次 private boolean cleanSomeSlots(int i, int n) {//新插入的位置i和實際大小n boolean removed = false;//並無真正的清除,只是找到了要清除的位置,而真正的清除在 expungeStaleEntry(int staleSlot) 裏面 Entry[] tab = table; int len = tab.length; do { i = nextIndex(i, len);//從i開始一個個向後看。循環log2(n)趟i一直加到i+log2(n),能夠循環回來。 Entry e = tab[i]; if (e != null && e.get() == null) { n = len;//若是在掃描過程當中遇到髒entry的話就會令n爲當前hash表的長度(n=len),再掃描log2(n)趟, //注意此時n增長無非就是多增長了循環次數,增長循環次數就是i向右增長到更大的位置而已。 removed = true; i = expungeStaleEntry(i);//清除i位置,而且i向右一直到null清除或者從新找位置。舊i到新i之間所有沒有髒數據了下次重新i開始就能夠了 } //執行 對數次數 數量的掃描,是一種 基於不掃描(快速但保留垃圾)和 全部元素掃描之間的平衡 } while ( (n >>>= 1) != 0);//n除以2,n用來控制掃描趟數(循環次數),在掃描過程當中,若是沒有遇到髒entry就整個掃描過程持續log2(n)次,log2(n)的得來是由於n >>>= 1,每次n右移一位至關於n除以2。 // return removed; } private void rehash() {//首先會清理陳舊的 Entry,若是清理完以後元素數量仍然大於 threshold 的 3/4,則進行擴容操做(數組大小變爲原來的 2倍) expungeStaleEntries(); if (size >= threshold - threshold / 4) //size >= 3/4*threshold, size >= 1/2*len resize();//擴容 } 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) {//遍歷過程當中若是遇到髒entry的話直接另value爲null,有助於value可以被回收 e.value = null; // 幫助GC } else {//從新hash int h = k.threadLocalHashCode & (newLen - 1); while (newTab[h] != null) h = nextIndex(h, newLen); newTab[h] = e; count++; } } } setThreshold(newLen); size = count; table = newTab; } /** */ private void expungeStaleEntries() { Entry[] tab = table; int len = tab.length; for (int j = 0; j < len; j++) {//遍歷全部的元素 Entry e = tab[j]; if (e != null && e.get() == null)//key爲null的有問題元素。 expungeStaleEntry(j); } } } }

 

相關文章
相關標籤/搜索