concurrethashmap的put操作

put操作大致可分爲以下幾個步驟:

1·計算key的hash值,即調用spread()方法計算hash值;

2·獲取hash值對應的Node節點位置,此時通過一個循環實現。有以下幾種情況:

3·A`如果table表爲空,則首先進行初始化操作,初始化之後再次進入循環獲取Node節點的位置;

3·B`如果table不爲空,但沒有找到key對應的Node節點,則直接調用casTabAt()方法插入一個新節點,此時使用CAS進行插入,
CAS失敗,說明有其它線程提前插入了,CAS會嘗試自旋重新插入;
,因爲第一次CAS失敗,CAS發現數據與原數據不同,會自旋重新插入,第二次進入修改數據爲想要插入的數據
CAS成功,則直接插入並計算addCount,判斷是否需要把鏈表升級爲紅黑樹。

3·C`如果table不爲空,且key對應的Node節點也不爲空,但Node頭節點點的hash值爲MOVED(-1)(hash值默認爲MOVED(-1)),則表示需要擴容,此時調用helpTransfer()方法進行擴容;(第五步補充:如果頭節點的hash值爲-1,說明當前f是ForwardingNode節點,意味有其它線程正在擴容,則一起進行擴容操作。我的理解是第一次擴容,頭節點才爲-1

3·D`其他情況下,則直接向Node中插入一個新Node節點,此時需要對這個Node鏈表或紅黑樹通過synchronized加鎖。
插入元素後,調用addCount()方法記錄table中元素的數量,判斷對應的Node結構是否需要改變結構,如果需要則調用treeifyBin()方法將Node鏈表升級爲紅黑樹結構;

synchronized (f) {
    if (tabAt(tab, i) == f) {
        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)))) {
                    oldVal = e.val;
                    if (!onlyIfAbsent)
                        e.val = value;
                    break;
                }
                Node<K,V> pred = e;
                if ((e = e.next) == null) {
                    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;
            }
        }
    }
}

put時,內部使用的方法


    //以volatile讀的方式讀取table數組中的元素
    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);
    }
    //以volatile寫的方式,將元素插入table數組(修改數據使用)
    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }
    //以CAS的方式,將元素插入table數組(上述步驟3·B)
    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        //原子的執行如下邏輯:如果tab[i]==c,則設置tab[i]=v,並返回ture.否則返回false
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

————————————————
推薦鏈接:JDK1.8,concurrethashmap詳解
最佳推薦:https://www.jianshu.com/p/5bc70d9e5410

可以參考https://www.jianshu.com/p/c0642afe03e0

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