併發容器學習——ConcurrentHashMap

1、ConcurrentHashMap併發容器java

1.ConcurrentHashMapnode

    ConcurrentHashMap是HashMap的升級版,HashMap雖然效率高,但它是線程不安全的容器,不能再多線程環境使用,而HashTable雖然是線程安全的,但使用的是效率比較低的synchronized,而且synchronized是悲觀鎖,不論讀寫都是獨佔的。而ConcurrentHashMap的底層是使用CAS來實現併發訪問的,效率較高。算法

    ConcurrentHashMap的底層數據結構與HashMap相同,採用數組+鏈表/紅黑樹來實現,以下圖:數組

2.鏈表結點的實現安全

    鏈表結點是基本的存儲單元,其實不用想太多,必然和HashMap中的Node相相似。數據結構

//能夠看到Node實現了Map.Entry接口,與HashMap基本同樣
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;    //hash值,final修飾,表示hash碼不可改變。
    final K key;    //key值
    volatile V val;    //value值,volatile保證可見性
    volatile Node<K,V> next;    //鏈表的下個結點

    Node(int hash, K key, V val, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.val = val;
        this.next = next;
    }

    public final K getKey()       { return key; }
    public final V getValue()     { return val; }
    public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
    public final String toString(){ return key + "=" + val; }
    public final V setValue(V value) {
        throw new UnsupportedOperationException();
    }

    //重寫equals方法
    public final boolean equals(Object o) {
        Object k, v, u; Map.Entry<?,?> e;
        return ((o instanceof Map.Entry) &&
                (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
                (v = e.getValue()) != null &&
                (k == key || k.equals(key)) &&
                (v == (u = val) || v.equals(u)));
    }

        //查找結點,能夠看出ConcurrentHashMap中不容許存放value==null的數據
    Node<K,V> find(int h, Object k) {
        Node<K,V> e = this;
        if (k != null) {
            do {
                K ek;
                if (e.hash == h &&
                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;
            } while ((e = e.next) != null);
        }
        return null;
    }
}

//底層數組擴容時的過渡鏈表結點類型
static final class ForwardingNode<K,V> extends Node<K,V> {
    final Node<K,V>[] nextTable;
    ForwardingNode(Node<K,V>[] tab) {
        super(MOVED, null, null, null);    //標識結點的狀態爲MOVED,即轉移擴容狀態
        this.nextTable = tab;
    }

    Node<K,V> find(int h, Object k) {
        // loop to avoid arbitrarily deep recursion on forwarding nodes
        outer: for (Node<K,V>[] tab = nextTable;;) {
            Node<K,V> e; int n;
            if (k == null || tab == null || (n = tab.length) == 0 ||
                (e = tabAt(tab, (n - 1) & h)) == null)
                return null;
            for (;;) {
                int eh; K ek;
                if ((eh = e.hash) == h &&
                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;
                if (eh < 0) {
                    if (e instanceof ForwardingNode) {
                        tab = ((ForwardingNode<K,V>)e).nextTable;
                        continue outer;
                    }
                    else
                        return e.find(h, k);
                }
                if ((e = e.next) == null)
                    return null;
            }
        }
    }
}

 

 
3.紅黑樹結點的實現
    紅黑樹結點的實現源碼:
 
//紅黑樹的結點實現,繼承了Node結點
static final class TreeNode<K,V> extends Node<K,V> {
    TreeNode<K,V> parent;  //父結點的引用
    TreeNode<K,V> left;    //左孩子
    TreeNode<K,V> right;    //右孩子
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;    //顏色


    TreeNode(int hash, K key, V val, Node<K,V> next,
             TreeNode<K,V> parent) {
        super(hash, key, val, next);
        this.parent = parent;
    }

    //查看樹中是否存在hash值爲h,value爲k的結點
    Node<K,V> find(int h, Object k) {
        return findTreeNode(h, k, null);
    }

    //查找結點
    final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
        //判斷k是否null,不容許放null值
        if (k != null) {    
            TreeNode<K,V> p = this;    
            do  {
                int ph, dir; K pk; TreeNode<K,V> q;
                TreeNode<K,V> pl = p.left, pr = p.right;
                if ((ph = p.hash) > h)
                    p = pl;
                else if (ph < h)
                    p = pr;
                else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
                    return p;
                else if (pl == null)
                    p = pr;
                else if (pr == null)
                    p = pl;
                else if ((kc != null ||
                          (kc = comparableClassFor(k)) != null) &&
                         (dir = compareComparables(kc, k, pk)) != 0)
                    p = (dir < 0) ? pl : pr;
                else if ((q = pr.findTreeNode(h, k, kc)) != null)
                    return q;
                else
                    p = pl;
            } while (p != null);
        }
        return null;
    }
}

 

 
    但ConcurrentHashMap中數組存放的紅黑樹根結點並非TreeNode類型,而是TreeBin類型,TreeNode被封裝在TreeBin中
 
//TreeBin 用做樹的頭結點,只存儲root和first節點,不存儲節點的key、value值
static final class TreeBin<K,V> extends Node<K,V> {
    TreeNode<K,V> root;    //根結點
    volatile TreeNode<K,V> first;    //樹的鏈式結構
    volatile Thread waiter;    //等待線程
    volatile int lockState;    //鎖狀態
    // values for lockState
    static final int WRITER = 1; // 表明擁有寫入鎖
    static final int WAITER = 2; // 表明等待獲取寫入鎖
    static final int READER = 4; // 用於設置讀取鎖的增長

}

 

4.繼承關係 
    知道了底層的數據結構,在來看看ConcurrentHashMap的繼承關係,以下圖所示,ConcurrentHashMap繼承了AbstractMap抽象類,實現了ConcurrentMap結構。其中AbstractMap包含了對Map集合的增刪改查等方法,以前對數據結構中HashMap和TreeMap的學習時已分析過,這裏再也不深刻。另外一個ConcurrentMap接口也相對簡單,提供幾個抽象的新方法,還是對Map集合的操做,不一樣的是這些操做要求是原子操做。
 
//ConcurrentMap接口源碼
public interface ConcurrentMap<K, V> extends Map<K, V> {
    //返回指定的key對應的value值,若key不存在則返回默認提供的defaultValue
    @Override
    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return ((v = get(key)) != null) ? v : defaultValue;
    }

        //遍歷Map的方法,至關於for循環或foreach循環,主要是用於lambda表達式
    @Override
    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                continue;
            }
            action.accept(k, v);
        }
    }

    //若是map中不存在key對應的鍵值對,那麼就新增當前傳入的key和value。
    //不然就返回map中key對應的value值
     V putIfAbsent(K key, V value);

    //刪除map中對應的key和value,當且僅當key和value都對應上時才刪除
    //若key的對應的值不是value則不刪除
    boolean remove(Object key, Object value);

        //替換key的value值,當key對應的舊值是oldValue時,替換爲newValue
        //不然不替換
    boolean replace(K key, V oldValue, V newValue);

        //若key存在,則將映射值替換爲給定的value
    V replace(K key, V value);

        //將每一個key映射的值替換成給定的調用函數的返回值
    @Override
    default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        forEach((k,v) -> {    //lambda表達式遍歷map
            while(!replace(k, v, function.apply(k, v))) {
                // v changed or k is gone
                if ( (v = get(k)) == null) {
                    // k is no longer in the map.
                    break;
                }
            }
        });
    }

    //若是map中不存在key(或key的映射爲null)且調用key爲參數的函數返回值newValue不爲null
    //且將key與返回值newValue關聯成功時,返回newValue,不然返回key本來的映射值
    @Override
    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v, newValue;
        return ((v = get(key)) == null &&
                (newValue = mappingFunction.apply(key)) != null &&
                (v = putIfAbsent(key, newValue)) == null) ? newValue : v;
    }

        //調用函數返回的新映射值替換key對應的舊映射值,若果新映射值爲null,則直接將鍵值對從map中刪除
    @Override
    default V computeIfPresent(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue;
        while((oldValue = get(key)) != null) {
            V newValue = remappingFunction.apply(key, oldValue);
            if (newValue != null) {
                if (replace(key, oldValue, newValue))
                    return newValue;
            } else if (remove(key, oldValue))
               return null;
        }
        return oldValue;
    }


    @Override
    default V compute(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue = get(key);    //獲取舊映射值
        for(;;) {
            V newValue = remappingFunction.apply(key, oldValue);    //計算新映射值
            if (newValue == null) {    //判斷新映射值是否爲null
                //新值不存在且map中存在key,直接刪除該鍵值對
                if (oldValue != null || containsKey(key)) {    
                    //判斷刪除是否成功
                    if (remove(key, oldValue)) {
                        // removed the old value as expected
                        return null;
                    }
                    //刪除失敗在繼續嘗試
                    oldValue = get(key);
                } else {
                    //key本來就不存在,直接返回
                    return null;
                }
            } else {
                // 新映射值不爲null,且舊映射值存在。則直接替換舊映射值
                if (oldValue != null) {
                    // replace
                    if (replace(key, oldValue, newValue)) {
                        // replaced as expected.
                        return newValue;
                    }

                    oldValue = get(key);
                } else {    //舊映射值不存在,即key不存在,則直接向map中新增
                    if ((oldValue = putIfAbsent(key, newValue)) == null) {
                        return newValue;
                    }

                }
            }
        }
    }

        //若是map中存在key且映射爲value,則用調用函數計算出的非空新映射值替換舊映射值
        //若新映設置爲null,則將舊鍵值對刪除
        //若是map中不存在key或key的舊映射爲null,則直接新增鍵值對(key和value)
    @Override
    default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        for (;;) {
            if (oldValue != null) {
                V newValue = remappingFunction.apply(oldValue, value);
                if (newValue != null) {
                    if (replace(key, oldValue, newValue))
                        return newValue;
                } else if (remove(key, oldValue)) {
                    return null;
                }
                oldValue = get(key);
            } else {
                if ((oldValue = putIfAbsent(key, value)) == null) {
                    return value;
                }
            }
        }
    }
}

 

    看完接口,在來看看ConcurrentHashMap中構造方法及一些重要的屬性變量,瞭解ConcurrentHashMap是如何初始化的。
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {
    //map可達到的最大容量,2的31次方
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    //map初始的默認容量,沒有指定map的容量時的默認值
    private static final int DEFAULT_CAPACITY = 16;

    //數組的最大長度
    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    //map的默認併發級別,沒有使用
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;

    //map的默認負載因子
    private static final float LOAD_FACTOR = 0.75f;

    //鏈表轉換成紅黑樹的臨界鏈表長度,即鏈表長度大於等於8時,鏈表將轉成紅黑樹
    static final int TREEIFY_THRESHOLD = 8;

    //紅黑樹轉成鏈表的臨界結點數,即紅黑樹的結點個數小於等於6時,由紅黑樹退化成鏈表
    static final int UNTREEIFY_THRESHOLD = 6;

        //容許鏈表樹化的最小容量值,即map的容量要大於64,才能將鏈表樹化
    static final int MIN_TREEIFY_CAPACITY = 64;

    //擴容線程所負責的區間大小最低爲16,避免發生大量的內存衝突
    private static final int MIN_TRANSFER_STRIDE = 16;

        //用於生成當前數組對應的基數戳
    private static int RESIZE_STAMP_BITS = 16;

    //表示最多能有多少個線程可以幫助進行擴容,由於sizeCtl只有低16位用於標識,因此最多隻有2^16-1個線程幫助擴容
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;

        //將基數戳左移的位數,保證左移後的基數戳爲負值,而後再加上n+1,表示n個線程正在擴容
    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
    static final int MOVED     = -1; // 表示正在轉移,是一個forwardNode節點
    static final int TREEBIN   = -2; // 表示已經轉換成樹,是一個TreeBin節點
    static final int RESERVED  = -3; // hash for transient reservations
    static final int HASH_BITS = 0x7fffffff; // 用於生成hash值
    
    //計算機的核心數
    static final int NCPU = Runtime.getRuntime().availableProcessors();

    //底層的table結點數組,存儲鍵值對
    transient volatile Node<K,V>[] table;

        //只有當數組處於擴容過程時,nextTable纔不爲null;不然其餘時刻,nextTable爲null; 
    //nextTable主要用於擴容過程當中指向擴容後的新數組
    private transient volatile Node<K,V>[] nextTable;
        
    /**
    * 未初始化:
    *     sizeCtl=0:表示沒有指定初始容量。
    *     sizeCtl>0:表示初始容量。
    * 初始化中:
    *     sizeCtl=-1,標記做用,告知其餘線程,正在初始化
    * 正常狀態:
    *     sizeCtl=0.75n ,擴容閾值
    * 擴容中:
    *     sizeCtl < 0 : 表示有其餘線程正在執行擴容
    *     sizeCtl = (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2 :表示此時只有一個線程在執行擴容
    */
    private transient volatile int sizeCtl;
    //用於擴容過程當中,指示原數組下一個分割區間的上界位置,從後往前指示
    private transient volatile int transferIndex;

        //key的視圖,map中全部key的集合
    private transient KeySetView<K,V> keySet;
    //value的視圖
    private transient ValuesView<K,V> values;
    //Entry視圖
    private transient EntrySetView<K,V> entrySet;

    //空構造,啥也沒幹
    public ConcurrentHashMap() {
    }

        //帶指定容量的構造方法,雖然指定了容量,但map的容量的肯定並非指定的容量
        //而是最接近該容量的2的冪次方數
    public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)    //判斷容量是否合法
                throw new IllegalArgumentException();
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
               MAXIMUM_CAPACITY :
               tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
        this.sizeCtl = cap;
    }
    
    //擴容策略,容量的肯定方法
    private static final int tableSizeFor(int c) {
        int n = c - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

        //帶有map集合的構造方法
    public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
        this.sizeCtl = DEFAULT_CAPACITY;
        putAll(m);
    }

    //帶有負載因子及初始化容量的構造方法
    public ConcurrentHashMap(int initialCapacity, float loadFactor) {
        this(initialCapacity, loadFactor, 1);
    }

    //帶有負載因子及初始化容量、併發等級的構造方法
    public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (initialCapacity < concurrencyLevel)   // 最低的容量不能小於併發級別
            initialCapacity = concurrencyLevel;   // as estimated threads
        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int)size);
        this.sizeCtl = cap;
    }

//不安全的屬性變量,知道就行

private static final sun.misc.Unsafe U;    //不安全的底層操做類
private static final long SIZECTL;    //變量sizeCtl的內存地址偏移量
private static final long TRANSFERINDEX;    //變量transferIndex的內存地址偏移量
private static final long BASECOUNT;    //變量baseCount的內存地址偏移量
private static final long CELLSBUSY;    //變量cellsBusy的內存地址偏移量
private static final long CELLVALUE;    //變量value的內存地址偏移量
private static final long ABASE;    //變量ak的內存地址偏移量
private static final int ASHIFT;    

static {
    try {
        U = sun.misc.Unsafe.getUnsafe();
        Class<?> k = ConcurrentHashMap.class;
        SIZECTL = U.objectFieldOffset
            (k.getDeclaredField("sizeCtl"));
        TRANSFERINDEX = U.objectFieldOffset
            (k.getDeclaredField("transferIndex"));
        BASECOUNT = U.objectFieldOffset
            (k.getDeclaredField("baseCount"));
        CELLSBUSY = U.objectFieldOffset
            (k.getDeclaredField("cellsBusy"));
        Class<?> ck = CounterCell.class;
        CELLVALUE = U.objectFieldOffset
            (ck.getDeclaredField("value"));
        Class<?> ak = Node[].class;
        ABASE = U.arrayBaseOffset(ak);
        int scale = U.arrayIndexScale(ak);
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
        ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
    } catch (Exception e) {
        throw new Error(e);
    }
}

}

 

    
5.put過程
    數據的put過程:
public V put(K key, V value) {
    return putVal(key, value, false);    //真正執行put的方法
}

//若key不存在map中,則直接將key與value新增到map中,若key已存在,則根據onlyIfAbsent
//決定是否覆蓋,false爲覆蓋,true不覆蓋
final V putVal(K key, V value, boolean onlyIfAbsent) {
    //判斷key或value是否爲null,從這能夠看出,ConcurrentHashMap的鍵與值均不能爲null
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());    //計算key對應的hash值
    int binCount = 0;    //用來計算在這個鏈表或紅黑樹總共有多少個元素,用來控制擴容或者轉移爲樹
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;

        //判斷table是否初始化過,若未初始化,則進行初始化
        if (tab == null || (n = tab.length) == 0)    
            tab = initTable();    //初始哈數組的方法

        //判斷hash值在table中對應的索引位置是否已經有數據
        //若沒有數據,則直接公國CAS的方式更新數據(即新建一個結點放到數組table中)
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))    //向數組中新增結點
                break;                   // 新增完畢,退出循環
        }
        //判斷數組table對應索引的結點的狀態(即鏈表的表頭或紅黑樹的根結點的hash值)
        //若爲MOVED表示數組正在擴容的複製階段,那麼當前線程也前去幫忙複製
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);    //幫助轉移複製數據
        else {
            V oldVal = null;
            
            //對錶頭或樹根結點對象加鎖,即同時僅有一個線程能對該鏈表或紅黑樹進行新增操做
            //從這裏能夠看出ConcurrentHashMap的併發安全是基於對鏈表或黑紅樹的獨佔操做實現的
            synchronized (f) {    
                //判斷數組中的索引是否發生改變
                //有可能結點的類型發生改變(鏈表轉成紅黑樹)
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        //遍歷鏈表
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            //判斷要存放的key在鏈表中是否已經存在
                            //如果存在則根據onlyIfAbsent決定是否覆蓋
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;

                            //如果key在鏈表中以前不存在,那麼新建一個key和value構成的結點,向鏈表末尾新增
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key,
                                                          value, null);
                                break;
                            }
                        }
                    }

                    //判斷結點是不是TreeBin類型,如果則代表鏈表已經樹化了
                    //應該使用紅黑樹中的方法進行替換或新增
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        //putTreeVal是紅黑樹中的新增或替換方法,
                        //若返回不爲null,則表示key在樹中已存在,返回對應的結點
                        //若返回是null,代表是新增結點
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                       value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }

            //判斷鏈表是否須要樹化成紅黑樹
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)    //判斷鏈表的長度是否超過8
                    treeifyBin(tab, i);    
                if (oldVal != null)        //如果替換結點的value,則返回舊值
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);    //到這必然新增一個node,該修改size的值了
    return null;
}

//hash值的計算方法,即散列算法
//h縮小2的16次方後與自身向異或,在與上HASH_BITS(0x7fffffff)
static final int spread(int h) {
    return (h ^ (h >>> 16)) & HASH_BITS;
}

//初始化table數組
private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    //當table爲空時,嘗試對table進行初始化
    while ((tab = table) == null || tab.length == 0) {
        //判斷是否有其餘線程正在對table數組進行初始化工做
        //當sizeCtl==-1時,說明已經有線程正在對table進行初始化,當前線程應該暫停讓出cpu資源
        if ((sc = sizeCtl) < 0)
            Thread.yield(); 
        
        //CAS方式嘗試更新sizeCtl的值,將sizeCtl的值由sc更新成-1,-1表示table表要進行初始化了
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                //再次確認table爲初始化過
                if ((tab = table) == null || tab.length == 0) {
                    //初始化指定的容量,若未指定初始容量的大小則使用DEFAULT_CAPACITY(16)
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;    
                    @SuppressWarnings("unchecked")
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                    table = tab = nt;
                    sc = n - (n >>> 2);    //令sizeCtl的值爲0.75倍的數組大小
                }
            } finally {
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}

//安全的獲取數組tab中索引爲i的位置的數據
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
    return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}

//輔助table數組的擴容轉移工做
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
    Node<K,V>[] nextTab; int sc;
    //判斷數組table是否爲null,且結點f是不是ForwardingNode類型,且nextTab是否爲null
    //即判斷map是否是處在擴容狀態,如果進入幫助擴容
    if (tab != null && (f instanceof ForwardingNode) &&
        (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
        int rs = resizeStamp(tab.length);
        while (nextTab == nextTable && table == tab &&
               (sc = sizeCtl) < 0) {
            //判斷是否還有未轉移的桶區間,若沒有,則退出
            if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                sc == rs + MAX_RESIZERS || transferIndex <= 0)
                break;
            //若還有,則嘗試參與擴容的線程數+1;成功就進行轉移,
            if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                transfer(tab, nextTab);
                break;
            }
        }
        return nextTab;
    }
    return table;
}

//鏈表是否須要樹化
private final void treeifyBin(Node<K,V>[] tab, int index) {
    Node<K,V> b; int n, sc;
    if (tab != null) {
        //判斷數組table的大小是否知足樹化的最小容量要求
        //若不知足,但鏈表長度又超過8,則進行數組擴容,擴大一倍
        if ((n = tab.length) < MIN_TREEIFY_CAPACITY)    
            tryPresize(n << 1);    //擴容
        else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
            //鎖結點將鏈表樹化。其實就是新建一棵紅黑樹。
            synchronized (b) {
                if (tabAt(tab, index) == b) {
                    TreeNode<K,V> hd = null, tl = null;
                    for (Node<K,V> e = b; e != null; e = e.next) {
                        TreeNode<K,V> p =
                            new TreeNode<K,V>(e.hash, e.key, e.val,
                                              null, null);
                        if ((p.prev = tl) == null)
                            hd = p;
                        else
                            tl.next = p;
                        tl = p;
                    }
                    setTabAt(tab, index, new TreeBin<K,V>(hd));    //更新索引位置的結點爲樹的根結點
                }
            }
        }
    }
}

//數組table擴容
private final void tryPresize(int size) {
    //判斷要擴大的容量是否越界,且是不是2的次方
    int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
        tableSizeFor(size + (size >>> 1) + 1);
    int sc;
    while ((sc = sizeCtl) >= 0) {
        Node<K,V>[] tab = table; int n;
        //判斷數組是否初始化過,若沒有初始化過,則初始化數組table
        //這裏和initTable方法是同樣的
        if (tab == null || (n = tab.length) == 0) {
            n = (sc > c) ? sc : c;
            if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if (table == tab) {
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
            }
        }

        //判斷是否須要擴容,c小於等於sc則表示無需擴容,沒達到擴容的臨界值,n大於MAXIMUM_CAPACITY表示不能再擴大了
        else if (c <= sc || n >= MAXIMUM_CAPACITY)
            break;
        else if (tab == table) {
            int rs = resizeStamp(n);    //不知道用來幹嗎,艹
            //判斷是否在擴容或初始化,擴容或初始化是sc小於0
            //如果初始化(sc==-1)
            if (sc < 0) {
                Node<K,V>[] nt;
               
                /**
                 * 1 (sc >>> RESIZE_STAMP_SHIFT) != rs :擴容線程數 > MAX_RESIZERS-1
                 * 2 sc == rs + 1 和 sc == rs + MAX_RESIZERS :看不懂..
                 * 3 (nt = nextTable) == null :表示nextTable正在初始化
                 * 4 transferIndex <= 0 :表示全部hash桶均分配出去
                 */
                //若是不須要幫其擴容,直接返回
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                    transferIndex <= 0)
                    break;

                //transfer的線程數+1,該線程將幫忙轉移
                //在transfer的時候,sc表示在transfer工做的線程數    
                //第一個執行擴容操做的線程,將sizeCtl設置爲:(resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2)
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                    transfer(tab, nt);
            }
            //沒有在初始化或擴容,則開始擴容
            else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                         (rs << RESIZE_STAMP_SHIFT) + 2))
                transfer(tab, null);
        }
    }
}

//這個方法計算n的二進制最高位前有幾個零,而後和2的15次方按位或,得出個數,不知道有什麼用
static final int resizeStamp(int n) {
    return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}

/**
* 數組擴容的核心方法
* 把數組中的節點複製到新的數組的相同位置,或者移動到擴張部分的相同位置
* 在這裏首先會計算一個步長,表示一個線程處理的數組長度,用來控制對CPU的使用,
* 每一個CPU最少處理16個長度的數組元素,也就是說,若是一個數組的長度只有16,那只有一個線程會對其進行擴容的複製移動操做
* 擴容的時候會一直遍歷,直到複製完全部節點,每處理一個節點的時候會在鏈表的頭部設置一個fwd節點,這樣其餘線程就會跳過他,
* 複製後在新數組中的鏈表不是絕對的反序的
* 簡要過程
* 一、獲取遍歷table的步長
* 二、最早進行擴容的線程,會初始化nextTable(正常狀況下,nextTable 是table的兩倍大小)
* 三、計算table某個位置索引 i,該位置上的數據將會被轉移到nextTable 中。
* 四、若是索引i 所對應的table的位置上沒有存放數據,則放有ForwardingNode 數據,代表該table 正在進行擴容處理。
*   (若是有添加數據的線程添加數據到該位置上,將會發現table的狀態)
* 五、將索引i 位置上的數據進行轉移,數據分紅兩部分,一部分就是數據 在nextTable 中索引沒有變(仍然是i),
*    另外一部分則是其索引變成i+n的,將這兩部分分別添加到nextTable 中。
*/
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
    int n = tab.length, stride;
    //判斷並設置每一個cpu核心(即每一個線程)須要轉移的結點數量,最少爲16個
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE; // subdivide range

    //nextTab爲null,初始化一個table數組2倍長度的數組
    if (nextTab == null) {            // initiating
        try {
            @SuppressWarnings("unchecked")
            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
            nextTab = nt;
        } catch (Throwable ex) {      // try to cope with OOME
            sizeCtl = Integer.MAX_VALUE;
            return;
        }
        nextTable = nextTab;
        transferIndex = n;    //transferIndex爲原數組的終點,轉移時,從後往前轉移,控制原數組的轉移
    }
    int nextn = nextTab.length;    //新數組長度

    //新建ForwardingNode結點,用來控制併發的,當一個節點爲空或已經被轉移以後,就設置爲ForwardingNode節點
    //是個空的標誌結點
    ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
    
    //advance 指的是作完了一個位置的遷移工做,能夠準備作下一個位置的了
    //表示是否繼續向前查找的標誌位
    boolean advance = true;    
    boolean finishing = false; //轉移是否完成的標誌位,完成前從新掃面數組,看看有沒有遺留的

    //計算和控制對table 表中位置i 的數據進行轉移
    //bound爲數組區間下限值,i爲當前轉移數組的位置,--i處理轉移下一個節點位置,從後往前處理
    //1 逆序遷移已經獲取到的hash桶集合,若是遷移完畢,則更新transferIndex,獲取下一批待遷移的hash桶
    //2 若是transferIndex=0,表示因此hash桶均被分配,將i置爲-1,準備退出transfer方法
    for (int i = 0, bound = 0;;) {
        Node<K,V> f; int fh;
        while (advance) {
            int nextIndex, nextBound;    
            
            //判斷當前這一段桶區間stride是否轉移完成
            //i小於bound,就表示當前任務完成了
            //finishing==true則表示沒有轉移任務結束了
            if (--i >= bound || finishing)
                advance = false;

            //判斷是否還有 轉移任務可領取
            //transferIndex==0表示,原數組中全部的桶區間都已經有線程在進行轉移了
            else if ((nextIndex = transferIndex) <= 0) {
                i = -1;
                advance = false;
            }
            
            //CAS操做修改transferIndex值,表明下一個線程要轉移原數組的結點的起始索引位置
            else if (U.compareAndSwapInt
                     (this, TRANSFERINDEX, nextIndex,
                      nextBound = (nextIndex > stride ?
                                   nextIndex - stride : 0))) {
                bound = nextBound;
                i = nextIndex - 1;
                advance = false;
            }
        }
        
        //判斷i是否小於0,便是不是全部數據都轉移完了
        //且i是否越界,i是不該該大於原數組長度的,也不應大於新數組長度
        if (i < 0 || i >= n || i + n >= nextn) {
            int sc;
            //判斷轉移操做是否完成
            //完成的話就將新數組賦給table,sizeCtl變爲0.75倍的新數組長度
            //nextTable則賦null,擴容完成
            if (finishing) {
                nextTable = null;
                table = nextTab;
                sizeCtl = (n << 1) - (n >>> 1);
                return;
            }

            //轉移工做沒有結束,嘗試更新sizeCtl的值,更新成功,表示當前轉移該桶區間數據的任務完成了
            /**     
            * 最早的線程,執行transfer方法時,會設置 
            * sizeCtl = (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2) (爲何是這個數?)
            * 後面輔助擴容的線程,執行transfer方法以前,會設置 sizeCtl = sizeCtl+1 
            * 每一個線程轉移完桶區間的數據後退出以前,會設置 sizeCtl = sizeCtl-1 
            * 那麼最後一個線程退出時:必然有sc == (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2),
            * 即 (sc - 2) == resizeStamp(n) << RESIZE_STAMP_SHIFT 
            */
            if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                //判斷是否到最後一個線程
                if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                    return;
                finishing = advance = true;
                i = n;     //最後退出的線程要從新check下是否所有轉移完畢
            }
        }

        //判斷原數組索引i處是否爲null,爲null就放個ForwardingNode結點,表示數組正在擴容轉移
        else if ((f = tabAt(tab, i)) == null)
            advance = casTabAt(tab, i, null, fwd);

        //判斷索引i處的結點是不是ForwardingNode結點,如果,表示該結點已經轉移了
        else if ((fh = f.hash) == MOVED)
            advance = true; // already processed
        else {

            //對數組索引i的結點進行加鎖,該結點進行數據轉移,不要打擾
            synchronized (f) {
                //再次判斷頭結點有沒有被修改過,被修改過就循環從新來
                if (tabAt(tab, i) == f) {
                    //新建兩個鏈表。用於存放原鏈表轉移到新數組是分開的結點
                    //ln存放索引不變的結點,hn存放索引變爲i+n的結點
                    Node<K,V> ln, hn;    

                    //判斷頭結點時鏈表仍是紅黑樹
                    /**
                    * fh大於等於0表示當前索引下是鏈表結構,那麼鏈表中的結點轉移到新數組中
                    * 只有可能存放在與元素組相同的索引i處,或是i+n(n爲原數組長度)處,這是由於
                    * 數組的擴容時原來的2倍,且數組的長度必須是2的冪次方,這就使得同一索引的結點在新數組
                    * 中的索引只有上述兩種可能的去處(這裏與hashmap是相同的)
                    */
                    if (fh >= 0) {
                        //計算hash值與數組長度的按位結果
                        /**
                        * 咱們知道hash&(n-1)是結點在數組中的索引位置
                        * 而hash&n,則是判斷結點是在新數組中位置是否變化的一個依據
                        * 例子:e.hash=6,二進制爲0000 0110。oldCap假設爲16,二進制位0001 0000。newCap爲32,二進制位0010 0000
                        * (e.hash & oldCap)=0000 0110 & 0001 0000=0000 0000 ;
                        * (e.hash & oldCap-1)=0000 0110 & 0000 1111=0000 0110 ;
                        * (e.hash & newCap)=0000 0110 & 0010 0000=0000 0000
                        * (e.hash & newCap-1)=0000 0110 & 0001 1111=0000 0110 ;
                        * 例子:e.hash=19,二進制爲0001 0011。oldCap假設爲16,二進制位0001 0000。newCap爲32,二進制位0010 0000
                        * (e.hash & oldCap)=0001 0011 & 0001 0000=0001 0000 ;
                        * (e.hash & oldCap-1)=0001 0011 & 0000 1111=0000 0011 ;
                        * (e.hash & newCap)=0001 0011 & 0010 0000=0000 0000
                        * (e.hash & newCap-1)=0001 0011 & 0001 1111=0001 0011 ;
                        * 有上面就能夠看出當(e.hash & oldCap) == 0時,(e.hash & oldCap-1)與(e.hash & newCap-1)的值時相同的,即在新數組中索引不變
                        * 而當(e.hash & oldCap) != 0 時,(e.hash & oldCap-1)與(e.hash & newCap-1)的值不相同的,即在新數組的索引變了
                        * 而且hash& n的結果只有0或n兩種,能夠藉此判斷是存放在hn中仍是ln中
                        int runBit = fh & n;    
                        Node<K,V> lastRun = f;    //最後一次發生hash值改變的結點
                        //遍歷鏈表查找結點的hash & n值最後一次有變化的結點
                        for (Node<K,V> p = f.next; p != null; p = p.next) {
                            int b = p.hash & n;
                            if (b != runBit) {
                                runBit = b;    
                                lastRun = p;
                            }
                        }

                        //lastRun用做哪一個鏈表的起點,runBit爲0表示結點在新數組中的索引不變
                        //runBit不爲0表示結點在新數組中發生改變,變爲i+n
                        if (runBit == 0) {
                            ln = lastRun;
                            hn = null;
                        }
                        else {
                            hn = lastRun;
                            ln = null;
                        }

                        //將結點轉移到新數組中
                        //遍歷舊鏈表,按個判斷是移動到i+n位置,仍是索引不動,分別放入ln或hn鏈表中
                        //這裏hn或ln哪一個鏈表爲null,就會出現結點順序反轉,假設ln爲null,則
                        //有ln==null,那麼越早加入鏈表就會排在越後面
                        //而hn==lastRun,則會有部分結點反轉,即原數組中lastRun結點以前的順序會反轉,以後不變
                        for (Node<K,V> p = f; p != lastRun; p = p.next) {
                            int ph = p.hash; K pk = p.key; V pv = p.val;
                            if ((ph & n) == 0)
                                ln = new Node<K,V>(ph, pk, pv, ln);
                            else
                                hn = new Node<K,V>(ph, pk, pv, hn);
                        }
                        //將兩個新的鏈表更新到新數組中
                        setTabAt(nextTab, i, ln);
                        setTabAt(nextTab, i + n, hn);
                        setTabAt(tab, i, fwd);    //將原數組的索引位置結點用fwd替換
                        advance = true;
                    }

                    //這裏就是紅黑樹的轉移了
                    else if (f instanceof TreeBin) {
                        TreeBin<K,V> t = (TreeBin<K,V>)f;
                        TreeNode<K,V> lo = null, loTail = null;
                        TreeNode<K,V> hi = null, hiTail = null;
                        int lc = 0, hc = 0;
                        for (Node<K,V> e = t.first; e != null; e = e.next) {
                            int h = e.hash;
                            TreeNode<K,V> p = new TreeNode<K,V>
                                (h, e.key, e.val, null, null);
                            if ((h & n) == 0) {
                                if ((p.prev = loTail) == null)
                                    lo = p;
                                else
                                    loTail.next = p;
                                loTail = p;
                                ++lc;
                            }
                            else {
                                if ((p.prev = hiTail) == null)
                                    hi = p;
                                else
                                    hiTail.next = p;
                                hiTail = p;
                                ++hc;
                            }
                        }
                        //判斷兩個樹結點是否是小於等於6,要不要退化成鏈表
                        ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                            (hc != 0) ? new TreeBin<K,V>(lo) : t;
                        hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                            (lc != 0) ? new TreeBin<K,V>(hi) : t;
                        setTabAt(nextTab, i, ln);
                        setTabAt(nextTab, i + n, hn);
                        setTabAt(tab, i, fwd);
                        advance = true;
                    }
                }
            }
        }
    }
}

 

    
6.size的更新
    到這ConcurrentHashMap的put過程及擴容過程的分析就算告一段落,可是仍然還有一點小問題尚未解決,那就是ConcurrentHashMap鍵值對個數的統計size值的問題,在putVal方法的末尾有個addCount方法就是對size的更新,但在看這個方法前先要了解size是如何在併發環境下進行定義的:
    //標識當前cell數組是否在初始化或擴容中的CAS標誌位
    private transient volatile int cellsBusy;

    //counterCells數組,總數值的分值分別存在每一個cell中
    private transient volatile CounterCell[] counterCells;


    //沒有競爭的時候使用,或者在初始化的時候做爲一個反饋
    //總和值的得到方式爲 base + 每一個cell中分值之和在併發度較低的場景下,全部值都直接累加在base中
    private transient volatile long baseCount;

//CounterCell的定義
@sun.misc.Contended static final class CounterCell {
    volatile long value;
    CounterCell(long x) { value = x; }
}

//計算總和,每一個CounterCell中的值相加
final long sumCount() {
    CounterCell[] as = counterCells; CounterCell a;
    long sum = baseCount;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    return sum;
}
 
 
    瞭解了定義,再來看看addCount方法:
private final void addCount(long x, int check) {
    CounterCell[] as; long b, s;
    
    //若是counterCell數組爲null,則直接嘗試將增長的鍵值對數更新到baseCount的上
    //若counterCell不爲null
    if ((as = counterCells) != null ||
        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        CounterCell a; long v; int m;

        // 是否衝突標誌,默認未衝突
        boolean uncontended = true;
        
        // 若是計數數組是空(還沒有出現併發)
        // 若是隨機取一個數組位置爲空 或者
        // 修改這個變量cellvalue失敗(出現併發了)
        // 執行 fullAddCount 方法。並結束
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
            !(uncontended =
              U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
            fullAddCount(x, uncontended);
            return;
        }
        if (check <= 1)
            return;
        //計算總和
        s = sumCount();
    }
    //判斷是否須要擴容,從putVal方法進來(check一定大於等於0)一定要檢查是否要擴容
    //這裏與上面分析的擴容過程相似,不在多說
    if (check >= 0) {
        Node<K,V>[] tab, nt; int n, sc;
        while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
               (n = tab.length) < MAXIMUM_CAPACITY) {
            int rs = resizeStamp(n);
            if (sc < 0) {
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                    transferIndex <= 0)
                    break;
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                    transfer(tab, nt);
            }
            else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                         (rs << RESIZE_STAMP_SHIFT) + 2))
                transfer(tab, null);
            s = sumCount();
        }
    }
}

/**
* 一、初始化probe,該值散列後用於獲取counterCells數組的索引值(線程相關)
* 二、若是counterCells數組不爲空,嘗試佔據counterCells 數組,建立CounterCell存放到counterCells中的某個位置上,退出循環
* 三、若是counterCells 中該位置上存在數據,若是有衝突,則從新循環,不然嘗試累加數據到cell 中,成功則退出循環。
* 四、若是counterCells 數組擴容,或者不須要擴容,則從新循環
* 五、對counterCells 進行擴容操做(容量增長1倍),而後從新循環
* 六、若是counterCells未初始化,則嘗試進行初始化
* 七、若是初始化失敗,則有其它線程再初始化,更新baseCount,成功就退出,不然從新循環。
*/
private final void fullAddCount(long x, boolean wasUncontended) {
    int h;
    // 獲取當前線程的probe值 
    // 若是爲0,則初始化當前線程probe值
    if ((h = ThreadLocalRandom.getProbe()) == 0) {
        ThreadLocalRandom.localInit();      // 生成一個線程全部的隨機值
        h = ThreadLocalRandom.getProbe();    //返回生成的probe值,用於選擇counterCells數組的下標
        wasUncontended = true;    //從新生成了probe,未衝突標誌位設置爲true
    }
    boolean collide = false;                // 標識是不是最後一個槽位
    for (;;) {
        CounterCell[] as; CounterCell a; int n; long v;

        //判斷counterCells數組是否初始化過,若未初始化則先初始化
        //cells數組不爲空,即表示初始化已經成功
        if ((as = counterCells) != null && (n = as.length) > 0) {
            //判斷當前線程對應的索引是否有數據
            //沒有數據則新建一個CounterCell存放x,並更新counterCells數組
            if ((a = as[(n - 1) & h]) == null) {
                //判斷counterCells數組在什麼狀態下
                //爲0表示不在擴容或初始化狀態
                if (cellsBusy == 0) {            // Try to attach new Cell
                    //新建當前線程對應的CounterCell對象
                    CounterCell r = new CounterCell(x); // Optimistic create
                    // CAS設置cellsBusy,防止其它線程來破壞數據結構
                    if (cellsBusy == 0 &&
                        U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                        boolean created = false;    //標誌位,操做是否成功
                        try {               // Recheck under lock
                            CounterCell[] rs; int m, j;
                            //更新counterCells數組中的數據
                            if ((rs = counterCells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                                rs[j] = r;
                                created = true;
                            }
                        } finally {
                            cellsBusy = 0;    //恢復標誌位
                        }
                        //更新成功,則退出死循環
                        if (created)
                            break;
                        continue;           // Slot is now non-empty
                    }
                }
                collide = false;
            }
            //對應索引中已經有數據了
            //而且調用該函數前,cellvalue的CAS操做也已經失敗(已經發生競爭)
            else if (!wasUncontended)       // CAS already known to fail
                wasUncontended = true;      // Continue after rehash

            //執行value的累加
            else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                break;    //成功更新就直接退出
            //若是數組比較大了,則不須要擴容,繼續重試,或者已經擴容了,重試
            else if (counterCells != as || n >= NCPU)
                collide = false;            // At max size or stale
            else if (!collide)
                collide = true;
            //對counterCells數組進行擴容,如有其餘線程在擴容則從新循環
            else if (cellsBusy == 0 &&
                     U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                try {
                    if (counterCells == as) {// Expand table unless stale
                        CounterCell[] rs = new CounterCell[n << 1];    //擴大1倍
                        for (int i = 0; i < n; ++i)    //直接轉移數據
                            rs[i] = as[i];
                        counterCells = rs;
                    }
                } finally {
                    cellsBusy = 0;
                }
                collide = false;
                continue;                   // Retry with expanded table
            }
            h = ThreadLocalRandom.advanceProbe(h);    //從新生成一個probe值
        }

        //進行初始化
        //線程要初始化counterCells數組的條件是:cellsBusy標誌位要爲0(說明不在初始化或擴容)
        //counterCells == as這個判斷不知道有什麼意義?
        //當前線程更新cellsBusy狀態爲初始化狀態,那麼其餘線程就不能再進行初始化了
        else if (cellsBusy == 0 && counterCells == as &&
                 U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
            boolean init = false;    //標誌初始化是否成功
            try {                           // Initialize table
                if (counterCells == as) {
                    CounterCell[] rs = new CounterCell[2];    //counterCells數組默認容量爲2
                    rs[h & 1] = new CounterCell(x);    //h&1是計算線程的probe值散列後的存放索引
                    counterCells = rs;
                    init = true;    //初始化成功
                }
            } finally {
                cellsBusy = 0;    //恢復
            }
            if (init)    //判斷初始化是否成功,成功就退出
                break;
        }

        //有其它線程佔據counterCells數組,直接累加在base變量中
        else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
            break;                          // Fall back on using base
    }
}

 

    到這size的大小是如何計算更新的就差很少分析完了,能夠看出size值只是一個瞬時值,大小隨時都有可能變化,只能作參考,不要過分依賴使用
    
7.get和remove的過程
    的剩餘的get方法和remove方法就很簡單了,看看就差很少都能理解,先來看看get方法:
//get方法的實現
public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
    int h = spread(key.hashCode());    //計算key對應的hash值

    //判斷table數組是否爲空,若不爲空,h對應的索引是否有結點
    //table不爲空或索引位置沒有結點說明key不存在,直接返回null
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (e = tabAt(tab, (n - 1) & h)) != null) {
        //判斷該索引位置的結點的hash值是否與h相同(有可能頭結點的hash值小於0,表示此時正在擴容)
        if ((eh = e.hash) == h) {
            //判斷要查找的key是否是頭結點,如果頭結點俺麼直接返回頭結點的value值
            if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                return e.val;
        }
        //eh小於0,該結點是ForwardingNode結點或TreeBin結點,調用find方法
        //ForwardingNode的find方法比較簡單就是遍歷新數組來查找對應結點是否存在
        //TreeBin的find方法則要複雜
        else if (eh < 0)
            return (p = e.find(h, key)) != null ? p.val : null;

        //遍歷鏈表來查找key對應的value
        while ((e = e.next) != null) {
            if (e.hash == h &&
                ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}

//ForwardingNode中的find方法,遍歷新數組對應的索引查找key
Node<K,V> find(int h, Object k) {
    // loop to avoid arbitrarily deep recursion on forwarding nodes
    outer: for (Node<K,V>[] tab = nextTable;;) {
        Node<K,V> e; int n;
        if (k == null || tab == null || (n = tab.length) == 0 ||
            (e = tabAt(tab, (n - 1) & h)) == null)
            return null;
        for (;;) {
            int eh; K ek;
            if ((eh = e.hash) == h &&
                ((ek = e.key) == k || (ek != null && k.equals(ek))))
                return e;
            if (eh < 0) {
                if (e instanceof ForwardingNode) {
                    tab = ((ForwardingNode<K,V>)e).nextTable;
                    continue outer;
                }
                else
                    return e.find(h, k);
            }
            if ((e = e.next) == null)
                return null;
        }
    }
}

//TreeBin中的find方法
final Node<K,V> find(int h, Object k) {
    if (k != null) {
        //
        for (Node<K,V> e = first; e != null; ) {
            int s; K ek;
            //判斷TreeBin的狀態,如果等待或寫狀態,則以鏈表的形式進行遍歷查找
            //((s = lockState) & (WAITER|WRITER)) == 0表示TreeBin如今處於讀狀態
            if (((s = lockState) & (WAITER|WRITER)) != 0) {
                if (e.hash == h &&
                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;
                e = e.next;
            }
            //嘗試更新讀狀態,即獲取讀鎖,成功的話,就以紅黑樹的形式進行遍歷查找
            else if (U.compareAndSwapInt(this, LOCKSTATE, s,
                                         s + READER)) {
                TreeNode<K,V> r, p;
                try {
                    p = ((r = root) == null ? null :
                         r.findTreeNode(h, k, null));
                } finally {
                    Thread w;
                    if (U.getAndAddInt(this, LOCKSTATE, -READER) ==
                        (READER|WAITER) && (w = waiter) != null)
                        LockSupport.unpark(w);
                }
                return p;
            }
        }
    }
    return null;
}

 

 
    再看remove方法:
public V remove(Object key) {
    return replaceNode(key, null, null);
}


final V replaceNode(Object key, V value, Object cv) {
    int hash = spread(key.hashCode());    //計算hash值
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        
        //判斷table數組是否爲空,或hash值對應索引上是否有結點
        //table爲空或索引上沒有結點,代表key不存在,直接返回
        if (tab == null || (n = tab.length) == 0 ||
            (f = tabAt(tab, i = (n - 1) & hash)) == null)
            break;
        else if ((fh = f.hash) == MOVED)    
            //若是當前table數組在擴容,當前線程先去輔助擴容,擴容完在刪除結點
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            boolean validated = false;
            synchronized (f) {    //加鎖,保證對應的鏈表或紅黑樹的併發操做
                if (tabAt(tab, i) == f) {    //再次判斷頭結點是否被改變
                    //判斷是鏈表仍是紅黑樹結構
                    //fh大於0代表是鏈表
                    if (fh >= 0) {    
                        validated = true;

                        //遍歷鏈表
                        for (Node<K,V> e = f, pred = null;;) {
                            K ek;
                            //判斷遍歷到的當前結點是否是要刪除的結點
                            //是要刪除的結點就進行刪除,並將刪除結點的value返回
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                V ev = e.val;
                                if (cv == null || cv == ev ||
                                    (ev != null && cv.equals(ev))) {
                                    oldVal = ev;
                                    if (value != null)
                                        e.val = value;
                                    else if (pred != null)
                                        pred.next = e.next;
                                    else
                                        setTabAt(tab, i, e.next);
                                }
                                break;
                            }
                            pred = e;
                            if ((e = e.next) == null)    //判斷下個結點
                                break;    //鏈表到頭了,說明不存在喲啊刪除的結點,直接退出循環
                        }
                    }
                    //底層是紅黑樹結構,執行紅黑樹中刪除節點的額方法,這裏不作深刻,有興趣能夠看源碼具體實現
                    else if (f instanceof TreeBin) {
                        validated = true;
                        TreeBin<K,V> t = (TreeBin<K,V>)f;
                        TreeNode<K,V> r, p;
                        
                        //先查找出key對應的結點是否存在,不存在直接結束
                        //存在的話,獲取到value再刪除結點
                        if ((r = t.root) != null &&
                            (p = r.findTreeNode(hash, key, null)) != null) {
                            V pv = p.val;
                            if (cv == null || cv == pv ||
                                (pv != null && cv.equals(pv))) {
                                oldVal = pv;
                                if (value != null)
                                    p.val = value;
                                else if (t.removeTreeNode(p))
                                    setTabAt(tab, i, untreeify(t.first));
                            }
                        }
                    }
                }
            }
            //判斷要刪除的key是否存在
            //存在就返回對應的value,不存在就返回null
            if (validated) {
                if (oldVal != null) {
                    if (value == null)
                        addCount(-1L, -1);
                    return oldVal;
                }
                break;
            }
        }
    }
    return null;
}

 

    OK,到這ConcurrentHashMap中的重要的方法基本就分析完了,剩餘的方法就更簡單,不在多說,gg。
相關文章
相關標籤/搜索