java8 ConcurrentHashMap源碼學習(2)


上文: java8 ConcurrentHashMap源碼學習.

ConcurrentHashMap

之前發現常用的remove方法還有helptransfer並沒有整理出來, 這裏把學習心得整理一下, 順便把get也貼上來

remove

其實這裏跟put一樣也是直接引用另一個方法

public V remove(Object key) {
        return replaceNode(key, null, null);
    }

這裏看到用了replaceNode方法入參是key, null, null
下面的代碼裏可以看見這三個參數的名稱
key就是map中的key, value就是要替換的值, cv就是要替換的key的value
如果value爲空, 那麼那麼就是替換key的value而不是做刪除
如果cv爲空那麼就不判斷oldValue直接進入替換或者刪除操作, 否則判斷cv是否與oldValue相等

final V replaceNode(Object key, V value, Object cv) {
        int hash = spread(key.hashCode());
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0 ||
                (f = tabAt(tab, i = (n - 1) & hash)) == null)
                break;
            // MOVED代表當前有遷移工作, 加入遷移
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                // 一個標誌位, 標誌是否有進行替換或者刪除工作
                boolean validated = false;
                synchronized (f) {
                	// 確保上鎖前後數據一致
                    if (tabAt(tab, i) == f) {
                    	// 置於這裏爲什麼fh>=0纔是鏈表, 因爲在構造紅黑樹的時候
                    	// 構造函數會將hash變量設置爲TREEBIN, 而TREEBIN的值是-2
                        if (fh >= 0) {
                            validated = true;
                            for (Node<K,V> e = f, pred = null;;) {
                                K ek;
                                // 找到這個key
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    V ev = e.val;
                                    // 上文提到的cv判斷
                                    if (cv == null || cv == ev ||
                                        (ev != null && cv.equals(ev))) {
                                        oldVal = ev;
                                        // 上文提到的value判斷替換或者刪除
                                        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
                            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;
                                    // 這裏removeTreeNode返回的bool不是代表刪除的成功與否
                                    // 這裏返回的是是否需要將紅黑樹轉換爲鏈表
                                    // 下文會寫到這個方法
                                    else if (t.removeTreeNode(p))
                                    	// 用untreeify方法將紅黑樹鏈表化
                                        setTabAt(tab, i, untreeify(t.first));
                                }
                            }
                        }
                    }
                }
                // 已經操作成功
                if (validated) {
                    if (oldVal != null) {
                    	// 這裏表示這是個刪除操作, 將table中的元素計數器-1
                        if (value == null)
                            addCount(-1L, -1);
                        return oldVal;
                    }
                    break;
                }
            }
        }
        return null;
    }

removeTreeNode

主要講一下返回的true和false, 紅黑樹操作按下不表, (因爲👴還不太清楚, 下次一定學

final boolean removeTreeNode(TreeNode<K,V> p) {
            TreeNode<K,V> next = (TreeNode<K,V>)p.next;
            TreeNode<K,V> pred = p.prev;  // unlink traversal pointers
            TreeNode<K,V> r, rl;
            // 在TreeBin裏面是有first屬性的, 因爲在treeifyBin這個方法中
            // 先構成雙鏈表, 然後傳入TreeBin的構造器中構造紅黑樹
            if (pred == null)
            	// 這裏pred=null代表着要刪除的p是第一個結點, first後移
                first = next;
            else
            	// 否則pred指向p的下一個
                pred.next = next;
            if (next != null)
            	// 雙鏈表操作
                next.prev = pred;
            // 特判first爲空的情況, 直接返回true表示將紅黑樹鏈表化, 出來的當然也是null
            if (first == null) {
                root = null;
                return true;
            }
            // 這裏是判斷紅黑樹大小, 沒有采用結點個數感覺挺奇怪的
            // 因爲就算紅黑樹中有十個結點也可以構成true的情況, 我下面貼個圖
            if ((r = root) == null || r.right == null || // too small
                (rl = r.left) == null || rl.left == null)
                return true;
            // 給紅黑樹上鎖進行刪除操作, 這下面涉及的就是紅黑樹的刪除操作, 雙鏈表操作上面做完了
            lockRoot();
            try {
                TreeNode<K,V> replacement;
                TreeNode<K,V> pl = p.left;
                TreeNode<K,V> pr = p.right;
                if (pl != null && pr != null) {
                    TreeNode<K,V> s = pr, sl;
                    while ((sl = s.left) != null) // find successor
                        s = sl;
                    boolean c = s.red; s.red = p.red; p.red = c; // swap colors
                    TreeNode<K,V> sr = s.right;
                    TreeNode<K,V> pp = p.parent;
                    if (s == pr) { // p was s's direct parent
                        p.parent = s;
                        s.right = p;
                    }
                    else {
                        TreeNode<K,V> sp = s.parent;
                        if ((p.parent = sp) != null) {
                            if (s == sp.left)
                                sp.left = p;
                            else
                                sp.right = p;
                        }
                        if ((s.right = pr) != null)
                            pr.parent = s;
                    }
                    p.left = null;
                    if ((p.right = sr) != null)
                        sr.parent = p;
                    if ((s.left = pl) != null)
                        pl.parent = s;
                    if ((s.parent = pp) == null)
                        r = s;
                    else if (p == pp.left)
                        pp.left = s;
                    else
                        pp.right = s;
                    if (sr != null)
                        replacement = sr;
                    else
                        replacement = p;
                }
                else if (pl != null)
                    replacement = pl;
                else if (pr != null)
                    replacement = pr;
                else
                    replacement = p;
                if (replacement != p) {
                    TreeNode<K,V> pp = replacement.parent = p.parent;
                    if (pp == null)
                        r = replacement;
                    else if (p == pp.left)
                        pp.left = replacement;
                    else
                        pp.right = replacement;
                    p.left = p.right = p.parent = null;
                }

                root = (p.red) ? r : balanceDeletion(r, replacement);

                if (p == replacement) {  // detach pointers
                    TreeNode<K,V> pp;
                    if ((pp = p.parent) != null) {
                        if (p == pp.left)
                            pp.left = null;
                        else if (p == pp.right)
                            pp.right = null;
                        p.parent = null;
                    }
                }
            } finally {
            	// 解鎖
                unlockRoot();
            }
            // 做紅黑樹的結構檢查
            assert checkInvariants(root);
            return false;
        }

Alt
圖貼上面了, 如果這一步理解有誤, 希望評論區可以指出

helpTransfer

算是最短的方法了吧??? 因爲邏輯跟transfer裏面差不多, 所以上次就沒去搭理

    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        Node<K,V>[] nextTab; int sc;
        // 判斷當前tab不爲空, 並且傳入結點正在遷移, 獲得擴容好的nextTable
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
            // 根據tab.length算出一個高十六位負數
            int rs = resizeStamp(tab.length);
            // 循環判斷獲得的數據是否不變, sizeCtl<0則說明正在遷移
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {
                // 遷移工作已經完成, 退出循環
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                    break;
                // 線程參與遷移, cas對sc+1表示參與遷移的線程數+1
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                    transfer(tab, nextTab);
                    break;
                }
            }
            return nextTab;
        }
        return table;
    }

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());
        // cas獲取當前的key所在的桶, 保證這個桶的數據是最新的
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
            // 判斷桶的第一個結點
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            // 這裏表示是紅黑樹
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            // 循環查找
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

之前find點進去還是鏈表的操作, 嚇了一跳, 結果發現不是在TreeNode裏面的, 下面纔是真正使用的find,
堂堂正正的紅黑樹搜索

Node<K,V> find(int h, Object k) {
            return findTreeNode(h, k, null);
        }

後記

沒有後記了, 如果還有下一篇再寫這篇後記

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