1. 程式人生 > >ConcurrentHashMap原始碼分析--Java8

ConcurrentHashMap原始碼分析--Java8

private transient volatile Node<K,V>[] nextTable//僅僅在擴容使用,並且此時非空

// 將table每一個bin(桶位)的Node移動或複製到nextTable
// 只在addCount(long x, int check)、helpTransfer、tryPresize中呼叫
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
    int n = tab.length, stride; 
    // 每核處理的量小於16,則強制賦值16
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE; // subdivide range
    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;
    }
    int nextn = nextTab.length;
    //連節點指標,標誌位,fwd的hash值為-1,fwd.nextTable=nextTab。
    ForwardingNode<K,V> fwd= new ForwardingNode<K,V>(nextTab);
    boolean advance= true;//併發擴容的關鍵屬性,等於true,說明此節點已經處理過
    boolean finishing = false; // to ensure sweep before committing nextTab
    for (int i = 0, bound = 0;;) { // 死迴圈
        Node<K,V> f; int fh;
        while (advance) { // 控制--i,遍歷原hash表中的節點
            int nextIndex, nextBound;
            if (--i >= bound || finishing)
                advance = false;
            else if ((nextIndex = transferIndex) <= 0) {
                i = -1;
                advance = false;
           }//TRANSFERINDEX 即用CAS計算得到的transferIndex
            else if (U.compareAndSwapInt
                     (this, TRANSFERINDEX, nextIndex,
                      nextBound = (nextIndex > stride ?
                                   nextIndex - stride : 0))) {
                bound = nextBound;
                i = nextIndex - 1;
                advance = false;
            }
        }
        if (i < 0 || i >= n || i + n >= nextn) {
            int sc;
            if (finishing) { // 所有節點複製完畢
                nextTable = null;
                table = nextTab;
                sizeCtl = (n << 1) - (n >>> 1); //擴容閥值設為原來的1.5倍,即現在的0.75倍
                return; // 僅有的2個跳出死迴圈出口之一
            }//CAS更新擴容閾值,sc-1表明新加入一個執行緒參與擴容
            if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                    return;// 僅有的2個跳出死迴圈出口之一
                finishing = advance = true;
                i = n; // recheck before commit
            }
        }
       else if ((f = tabAt(tab, i)) == null) //該節點為空,則插入ForwardingNode
            advance = casTabAt(tab, i, null, fwd);
        //遍歷到ForwardingNode節點,說明此節點被處理過了,直接跳過。這是控制併發擴容的核心 
       else if ((fh = f.hash) == MOVED) // MOVED=-1,hash for fwd
            advance = true; // already processed
       else {
            synchronized (f) { //上鎖
                if (tabAt(tab, i) == f) {
                    Node<K,V> ln, hn; //ln原位置節點,hn新位置節點
                    if (fh >= 0) { // 連結串列
                        int runBit = fh & n; // f.hash & n
                        Node<K,V> lastRun = f; // lastRun和p兩個連結串列,逆序??
                        for (Node<K,V> p = f.next; p != null; p = p.next) {
                            int b = p.hash & n; // f.next.hash & n
                            if (b != runBit) {
                                runBit = b;
                                lastRun = p;
                            }
                        }
                        if (runBit == 0) {
                            ln = lastRun;
                            hn = null;
                        }
                        else {
                            hn = lastRun;
                            ln = null;
                        }
                        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) // 和HashMap確定擴容後的節點位置一樣
                                ln = new Node<K,V>(ph, pk, pv, ln);
                            else
                                hn = new Node<K,V>(ph, pk, pv, hn); //新位置節點
                        }//類似HashMap,為何i+n?參見HashMap的筆記
                        setTabAt(nextTab, i, ln);//在nextTable[i]插入原節點
                        setTabAt(nextTab, i + n, hn);//在nextTable[i+n]插入新節點
                        //在nextTable[i]插入forwardNode節點,表示已經處理過該節點 
                        setTabAt(tab, i, fwd);
                        //設定advance為true 返回到上面的while迴圈中 就可以執行--i操作
                        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;
                        //lc、hc=0兩計數器分別++記錄原、新bin中TreeNode數量
                        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;
                    }
                }
            }
        }
    }
}

相關推薦

ConcurrentHashMap原始碼分析--Java8

private transient volatile Node<K,V>[] nextTable; //僅僅在擴容使用,並且此時非空 // 將table每一個bin(桶位)的Node移動或複製到nextTable // 只在addCount(long x, int check)、helpTr

JDK1.8 ConcurrentHashMap原始碼分析

文章目錄 ConcurrentHashMap資料結構 類的繼承關係 類的內部類 重要的屬性 類的建構函式 ConcurrentHashMap()型建構函式 ConcurrentHashMap(i

ConcurrentHashMap原始碼分析

本篇部落格的目錄: 一:put方法原始碼 二:get方法原始碼 三:rehash的過程 四:總結 一:put方法的原始碼 首先,我們來看一下segment內部類中put方法的原始碼,這個方法它是segment片組的,也就是我們在用concurrentHash的p

原始碼閱讀系列】JDK 8 ConcurrentHashMap 原始碼分析之 由transfer引發的bug

不閱讀原始碼就不會發現這個事兒 前段時間在閱讀ConcurrentHashMap原始碼,版本JDK 8,目前原始碼研究已經告一段落。感謝魯道的ConcurrentHashMap原始碼分析文章,讀到文章,感覺和作者發生了一些交流,解答了很多疑惑,也驗證了一些想法。魯道在簡書的addCount分析文章點這裡&n

(Map) HashMap、LinkedHashMap、ConcurrentHashMap原始碼分析

前言 聽說Hashtabe、HashMap、LinkedHashMap、ConcurrentHashMap面試被問較多,平時也會去注意,但畢竟還是要自己看看原始碼,這樣才能在開發過程中選擇最合適的資料結構,簡單來說, HashMap:陣列+單鏈表,陣列(桶,

ArrayList原始碼分析-java8

1.特點總結: 可儲存元素null 呼叫無參構造器建立例項,預設容量capacity=10 自動擴容倍數:1.5 和Vector類等價,區別是 ArrayList不是執行緒安全的. 4個重要的private static final型別

Set介面原始碼分析-java8

1.toArray()和toArray(T[] a) 將set例項轉為String[]的方式如下: Set<String> x=new HashSet<String>; String[] y = x.toArray(new S

【java基礎之ConcurrentHashMap原始碼分析

概述: ConcurrentHashMap這個類在java.lang.current包中,這個包中的類都是執行緒安全的。Conc

多執行緒高併發程式設計(10) -- ConcurrentHashMap原始碼分析

  一.背景   前文講了HashMap的原始碼分析,從中可以看到下面的問題: HashMap的put/remove方法不是執行緒安全的,如果在多執行緒併發環境下,使用synchronized進行加鎖,會導致效率低下; 在遍歷迭代獲取時進行修改(put/remove)操作,會導致發生併發修改異常(Concu

Java8原始碼分析】併發包-ConcurrentHashMap(一)

一、CAS原理簡介 Java8中,ConcurrentHashMap摒棄了Segment的概念,而是啟用了一種全新的方式實現:利用CAS演算法。它沿用了HashMap的思想,底層依然由“陣列”+連結串列+紅黑樹的方式實現。 那什麼CAS演算法呢?以前採用鎖的

Java原始碼分析——ConcurrentHashMap

HashMap在高併發情況下新增值容易出現環形鏈,不能保證資料的安全性,所以ConcurrentHashMap作為替代方式出現了,concurrentHashmap比hashtable更加高效,因為hashtable是將整個物件鎖住,當一個執行緒在操作時,其它執行緒不能有任何操作。

ConcurrentHashMap與紅黑樹實現分析Java8

本文學習知識點 1、二叉查詢樹,以及二叉樹查詢帶來的問題。 2、平衡二叉樹及好處。 3、紅黑樹的定義及構造。 4、ConcurrentHashMap中紅黑樹的構造。 在正式分析紅黑樹之前,有必要了解紅黑樹的發展過程,請讀者耐心閱讀。 二叉查詢樹 紅黑樹的起源得從二叉查詢

HashMap、ConcurrentHashMap實現原理及原始碼分析

HashMap:https://www.cnblogs.com/chengxiao/p/6059914.html ConcurrentHashMap:https://blog.csdn.net/dingjianmin/article/details/79776646   遺留問

ConcurrentHashMap JDK1.8中結構原理及原始碼分析

注:本文根據網路和部分書籍整理基於JDK1.7書寫,如有雷同敬請諒解  歡迎指正文中的錯誤之處。 資料結構       ConcurrentHashMap 1.8 拋棄了Segment分段鎖機制,採用Node + CAS + Synchronized來保證併發安全進行實現

【java基礎】ConcurrentHashMap實現原理及原始碼分析

  ConcurrentHashMap是Java併發包中提供的一個執行緒安全且高效的HashMap實現(若對HashMap的實現原理還不甚瞭解,可參考我的另一篇文章),ConcurrentHashMap在併發程式設計的場景中使用頻率非常之高,本文就來分析下Concurre

ConcurrentHashMap實現原理及原始碼分析

ConcurrentHashMap是Java併發包中提供的一個執行緒安全且高效的HashMap實現(若對HashMap的實現原理還不甚瞭解,可參考我的另一篇文章),ConcurrentHashMap在併發程式設計的場景中使用頻率非常之高,本文就來分析下Concurrent

ConcurrentHashMap鎖分段技術原始碼分析

一、背景: 執行緒不安全的HashMap     因為多執行緒環境下,使用Hashmap進行put操作會引起死迴圈,導致CPU利用率接近100%,所以在併發情況下不能使用HashMap。 效率低下的HashTable容器      HashTable容器使用syn

JUC原始碼分析-集合篇(一):ConcurrentHashMap

ConcurrentHashMap 是一個支援併發檢索和併發更新的執行緒安全的HashMap(但不允許空key或value)。不管是在實際工作或者是面試中,ConcurrentHashMap 都是在整個JUC集合框架裡出現頻率最高的一個類,所以,對ConcurrentHas

JDK原始碼分析系列--ConcurrentHashMap(1.8)

定義變數 /** * node陣列最大容量 */ private static final int MAXIMUM_CAPACITY = 1 << 30; /** * 預設容量 */ pr

HashMap, ConcurrentHashMap 最詳細的原理及原始碼分析

網上關於 HashMap 和 ConcurrentHashMap 的文章確實不少,不過缺斤少兩的文章比較多,所以才想自己也寫一篇,把細節說清楚說透,尤其像 Java8 中的 ConcurrentHashMap,大部分文章都說不清楚。 終歸是希望能降低大家學習的成本,不希望大家到處找各種不是很