ConcurrentHashMap

1.sizeCtl

  • 等于0時(shí),表示數(shù)組還沒有進(jìn)行初始化(即數(shù)組為null)
  • 大于0時(shí),如果數(shù)組還沒有進(jìn)行初始化,表示數(shù)組的長度,如果數(shù)組已經(jīng)完成初始化,表示擴(kuò)容的閾值
  • 小于0時(shí),如果為-1,表示正在數(shù)組初始化,否則設(shè)該值x = - (1 + n) 那么有n個(gè)線程在協(xié)助擴(kuò)容

2.initTable

  • 如果table==null ,再判斷sizeCtl是否為-1,如果是則調(diào)用Thread.yeild()重新回到就緒態(tài)
  • 否則通過cas將sizeCtl設(shè)置為-1,并初始化數(shù)組,將sc設(shè)為擴(kuò)容閾值
private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        // 如果 sizeCtl < 0 ,說明另外的線程執(zhí)行CAS 成功,正在進(jìn)行初始化盅惜。
        if ((sc = sizeCtl) < 0)
            // 讓出 CPU 使用權(quán)
            Thread.yield(); // lost initialization race; just spin
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                if ((tab = table) == null || tab.length == 0) {
                    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);
                }
            } finally {
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}

3. putVal()

  • key == null || value == null 拋出異常
  • 數(shù)組沒有進(jìn)行初始化,則進(jìn)行初始化
  • 如果對(duì)應(yīng)桶沒有存放元素,使用cas來new一個(gè)Node,并賦新值
  • 如果桶中第一個(gè)元素的hash值為-1,表示正在擴(kuò)容,當(dāng)前線程回去協(xié)助擴(kuò)容
  • 否則對(duì)桶中的第一個(gè)元素作為synchronized對(duì)象鎖.
    判斷如果是鏈表(元素哈希值大于0),則遍歷鏈表,如果遇到f的hash值相等,且使用==判斷相等的元素或equals等,則替換value,并break.否則該元素是新的,則向最后插入.
    如果是紅黑樹則調(diào)用紅黑樹的插入方法.
  • 判斷是否需要將鏈表轉(zhuǎn)為紅黑樹
  • 調(diào)用addCount處理當(dāng)前Node的個(gè)數(shù)和是否需要擴(kuò)容
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;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            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
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                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;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

4. 計(jì)算元素個(gè)數(shù)count

  • 每個(gè)線程有一個(gè)隨機(jī)值,類似與hashCode
  • 數(shù)組如果為空员串,設(shè)置cellsBusy=0,new一個(gè)長度為2的數(shù)據(jù)芍耘,new里面的Cell對(duì)象
  • 如果數(shù)組為空苔可,判斷數(shù)組是否在被創(chuàng)建(cellsBusy=1)召边,這時(shí)另一個(gè)線程來了粪滤,數(shù)組為空官套,且busy,那么使用cas加baseCount,加成功則結(jié)束
  • 數(shù)組不為空盛正,判斷哈希值&(len-1)對(duì)應(yīng)的位置有沒有對(duì)象删咱,沒有則創(chuàng)建CounterCell對(duì)象,參數(shù)即為傳入的1豪筝。如果成功cas將忙由0改為1痰滋,那么會(huì)將數(shù)組中的對(duì)應(yīng)位置賦值
  • 如果有對(duì)象,則cas加1续崖,如果cas失敗敲街,將會(huì)觸發(fā)擴(kuò)容為2倍,如果數(shù)組長度大于等于cpu核數(shù)严望,如果不能繼續(xù)擴(kuò)容了多艇,換個(gè)位置加。
private final void fullAddCount(long x, boolean wasUncontended) {
        int h;
        if ((h = ThreadLocalRandom.getProbe()) == 0) {
            ThreadLocalRandom.localInit();      // force initialization
            h = ThreadLocalRandom.getProbe();
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
        for (;;) {
            CounterCell[] as; CounterCell a; int n; long v;
            if ((as = counterCells) != null && (n = as.length) > 0) {
                if ((a = as[(n - 1) & h]) == null) {
                    if (cellsBusy == 0) {            // Try to attach new Cell
                        CounterCell r = new CounterCell(x); // Optimistic create
                        if (cellsBusy == 0 &&
                            U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                            boolean created = false;
                            try {               // Recheck under lock
                                CounterCell[] rs; int m, j;
                                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;
                }
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                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;
                else if (cellsBusy == 0 &&
                         U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                    try {
                        if (counterCells == as) {// Expand table unless stale
                            CounterCell[] rs = new CounterCell[n << 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);
            }
            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];
                        rs[h & 1] = new CounterCell(x);
                        counterCells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
                break;                          // Fall back on using base
        }
    }

5. transfer

  • 判斷每個(gè)線程負(fù)責(zé)多少個(gè)桶,如果單核cpu,負(fù)責(zé)所有桶.否則計(jì)算原數(shù)組長度len/(8*cpu核數(shù)),如果小于16,則為16,否則為該值
  • 一個(gè)線程負(fù)責(zé)這些桶,如果做完了,繼續(xù)做,做之前讓sizectl+1,做完了讓sizectl-1,最后判斷兩個(gè)相等則結(jié)束.
  • 一個(gè)線程對(duì)一個(gè)桶遷移時(shí),會(huì)使用桶的第一個(gè)元素加鎖,第一次遍歷會(huì)找到鏈表末尾,對(duì)于hash后還在原來index的連續(xù)的末尾的元素,直接放到新數(shù)組的位置,其余的復(fù)制到兩個(gè)對(duì)應(yīng)位置.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末著蟹,一起剝皮案震驚了整個(gè)濱河市墩蔓,隨后出現(xiàn)的幾起案子梢莽,更是在濱河造成了極大的恐慌,老刑警劉巖奸披,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昏名,死亡現(xiàn)場離奇詭異,居然都是意外死亡阵面,警方通過查閱死者的電腦和手機(jī)轻局,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來样刷,“玉大人仑扑,你說我怎么就攤上這事≈帽牵” “怎么了镇饮?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長箕母。 經(jīng)常有香客問我储藐,道長,這世上最難降的妖魔是什么嘶是? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任钙勃,我火速辦了婚禮,結(jié)果婚禮上聂喇,老公的妹妹穿的比我還像新娘辖源。我一直安慰自己,他們只是感情好希太,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布克饶。 她就那樣靜靜地躺著,像睡著了一般跛十。 火紅的嫁衣襯著肌膚如雪彤路。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天芥映,我揣著相機(jī)與錄音洲尊,去河邊找鬼。 笑死奈偏,一個(gè)胖子當(dāng)著我的面吹牛坞嘀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惊来,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼丽涩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起矢渊,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤继准,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后矮男,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體移必,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年毡鉴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了崔泵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猪瞬,死狀恐怖憎瘸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陈瘦,我是刑警寧澤幌甘,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站痊项,受9級(jí)特大地震影響含潘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜线婚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盆均。 院中可真熱鬧塞弊,春花似錦、人聲如沸泪姨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肮砾。三九已至诀黍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仗处,已是汗流浹背眯勾。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留婆誓,地道東北人吃环。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像洋幻,于是被迫代替她去往敵國和親郁轻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 1.ConcurrentHashmap簡介 在使用HashMap時(shí)在多線程情況下擴(kuò)容會(huì)出現(xiàn)CPU接近100%的情況...
    你聽___閱讀 4,639評(píng)論 2 20
  • ConcurrentHashMap 的初步使用及場景 CHM 的使用 ConcurrentHashMap 是 J....
    悠娜的奶爸閱讀 279評(píng)論 0 1
  • 1 并發(fā)安全的哈希 為什么需要ConcurrentHashMap HashMap在多線程環(huán)境下非線程安全,而Has...
    Jw_Summary閱讀 347評(píng)論 0 0
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月好唯,有人笑有人哭竭沫,有人歡樂有人憂愁,有人驚喜有人失落骑篙,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,531評(píng)論 28 53
  • 信任包括信任自己和信任他人 很多時(shí)候蜕提,很多事情,失敗替蛉、遺憾贯溅、錯(cuò)過,源于不自信躲查,不信任他人 覺得自己做不成它浅,別人做不...
    吳氵晃閱讀 6,187評(píng)論 4 8