JDK8中ConcurrentHashMap源碼解析


在介紹ConcurrentHashMap源碼之前,首先需要了解以下幾個知識

1、JDK1.8中ConcurrentHashMap的基本結構

2、並發編程的三個概念:可見性,原子性,有序性

3、CAS(CompareAndSwap):比較和交換,是原子性操作,屬於樂觀鎖的一種實現。

4、java中的鎖機制

 

先簡單看下ConcurrentHashMap類在jdk1.8中的設計,由數組+鏈表+紅黑樹構成,其基本結構如圖所示:

/**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
   * 數組結構。采用第一次插入式才初始化的懶惰模式。容量總是2的冪次方。可通過迭代直接訪問。
*/ transient volatile Node<K,V>[] table; // volatile保證線程間可見
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash; 
        final K key;
        volatile V val;
        volatile Node<K,V> next;
}

 

接下來看一下初一些重要的初始化變量

/**
     * 初始化默認容量
     */
    private static final int DEFAULT_CAPACITY = 16; 
/**
   * 最大容量
*/ private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
     * 當容量達到當前最大容量的75%時,進行擴容操作
     */
    private static final float LOAD_FACTOR = 0.75f;
/**
     * 當鏈表長度>=8,轉換為紅黑樹
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 當紅黑樹節點個數<=6,轉換為鏈表
     */
    static final int UNTREEIFY_THRESHOLD = 6;
/**
 * 記錄容器的容量大小,通過CAS更新
 */
private transient volatile long baseCount;

/**
 * 初始化和擴容控制參數。為負數時表示table正在被初始化或resize:-1(初始化),-(1+擴容線程數)
* sizeCtl默認值為0,大於0是擴容的閥值
*/ private transient volatile int sizeCtl;

 

最后我們從put方法正式進入源碼分析

    final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
     // while(true)循環,不斷的嘗試,因為在table的初始化和casTabAt用到了compareAndSwapInt、compareAndSwapObject
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
       // 如果數組(HashMap)還未初始化,進行初始化操作(CAS操作)
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
       // 計算新插入數據在數組(HashMap)中的位置,如果該位置上的節點為null,直接放入數據(CAS操作)
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
       // 如果該節點的hash值為MOVED,說明正在進行擴容操作或者已經擴容
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
       // 
            else {
                V oldVal = null;
                synchronized (f) {  // 對特定數組節點tab[i]加鎖
                    if (tabAt(tab, i) == f) { // 判斷tab[i]是否有變化
                        if (fh >= 0) { // 插入操作
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) { // 遍歷鏈表
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) { // 如果新插入值和tab[i]處的hash值和key值一樣,進行替換
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) { // 如果此節點為尾部節點,把此節點的next引用指向新數據節點
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) { // 如果是一顆紅黑樹
                            Node<K,V> p;
                            binCount = 2;
                            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) //如果數組節點的鏈表長度超過限定長度,轉換為一顆紅黑樹
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount); 
        return null;
    }

上面代碼已經關鍵部分已經做了注釋,一些函數不再細講。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM