Java基礎(chǔ)(六)-ConcurrentHashMap線程安全實現(xiàn)

HashMap是線程不安全的,因此為了解決線程安全問題崇堵,提出了兩個類:HashTable和CurrentHashMap。

HashTable相關(guān)操作都是對方法加synchronized的大鎖,效率比較低采缚。ConcurrentHashMap避免了對全局加鎖改成了局部加鎖操作瓶竭,這樣就極大地提高了并發(fā)環(huán)境下的操作速度督勺,由于ConcurrentHashMap在JDK1.7和1.8中的實現(xiàn)非常不同渠羞,接下來我們談談JDK在1.7和1.8中的區(qū)別。

一智哀、1.7版本

在JDK1.7中ConcurrentHashMap采用了數(shù)組+Segment+分段鎖的方式實現(xiàn)次询。在數(shù)組基礎(chǔ)上增加了一層Segment,一個Segment對應數(shù)組的一段瓷叫,這樣對某段進行的操作只需要鎖住對應段屯吊,不影響其他段的操作。其中摹菠,Segment繼承了ReentrantLock并實現(xiàn)了序列化接口盒卸,說明Segment的鎖是可重入的。

iJDK1.7中ConcurrentHashMap數(shù)據(jù)結(jié)構(gòu)

缺點:
ConcurrentHashMap比HashMap多了一次hash過程次氨,第1次hash定位到Segment蔽介,第2次hash定位到HashEntry,然后鏈表搜索找到指定節(jié)點煮寡。

優(yōu)點:
在進行寫操作時虹蓄,只需鎖住寫元素所在的Segment即可,其他Segment無需加鎖幸撕,提高了并發(fā)讀寫的效率武花。

二、1.8版本

在JDK1.7中ConcurrentHashMap杈帐,數(shù)據(jù)結(jié)構(gòu)上体箕,首先整體上是數(shù)組+鏈表+紅黑樹的結(jié)構(gòu)與HashMap保持一致,其次取消了Segment分段鎖的數(shù)據(jù)結(jié)構(gòu)挑童,取而代之的是Node累铅,Node的value和next都是由volatile關(guān)鍵字進行修飾,可以保證可見性站叼。將細化的粒度從段進一步降低到節(jié)點娃兽。線程安全實現(xiàn)上,采用CAS+Synchronized替代Segment分段鎖尽楔。

簡單看下put過程:

/** Implementation for put and putIfAbsent */
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;
                       }
                    }
                    else if (f instanceof ReservationNode)
                        throw new IllegalStateException("Recursive update");
               }
            }
           ...
   return null;
}

ConcurrentHashMap會判斷tabAt(tab, i = (n - 1) & hash)是不是 null投储,是的話就直接采用CAS進行插入,而如果不為空的話阔馋,則是synchronized鎖住當前Node的首節(jié)點玛荞,這是因為當該Node不為空的時候,證明了此時出現(xiàn)了Hash碰撞呕寝,就會涉及到鏈表的尾節(jié)點新增或者紅黑樹的節(jié)點新增以及紅黑樹的平衡勋眯,這些操作自然都是非原子性的。從而導致無法使用CAS,當Node的當前下標為null的時候客蹋,由于只是涉及數(shù)組的新增塞蹭,所以用CAS即可。

參考:
https://baijiahao.baidu.com/s?id=1617089947709260129&wfr=spider&for=pc

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末讶坯,一起剝皮案震驚了整個濱河市番电,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辆琅,老刑警劉巖钧舌,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涎跨,居然都是意外死亡,警方通過查閱死者的電腦和手機崭歧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門隅很,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人率碾,你說我怎么就攤上這事叔营。” “怎么了所宰?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵绒尊,是天一觀的道長。 經(jīng)常有香客問我仔粥,道長婴谱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任躯泰,我火速辦了婚禮谭羔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘麦向。我一直安慰自己瘟裸,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布诵竭。 她就那樣靜靜地躺著话告,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卵慰。 梳的紋絲不亂的頭發(fā)上沙郭,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音裳朋,去河邊找鬼棠绘。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的氧苍。 我是一名探鬼主播夜矗,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼让虐!你這毒婦竟也來了紊撕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤赡突,失蹤者是張志新(化名)和其女友劉穎对扶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惭缰,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡浪南,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了漱受。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片络凿。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昂羡,靈堂內(nèi)的尸體忽然破棺而出絮记,到底是詐尸還是另有隱情,我是刑警寧澤虐先,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布怨愤,位于F島的核電站,受9級特大地震影響蛹批,放射性物質(zhì)發(fā)生泄漏撰洗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一腐芍、第九天 我趴在偏房一處隱蔽的房頂上張望了赵。 院中可真熱鬧,春花似錦甸赃、人聲如沸柿汛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽络断。三九已至,卻和暖如春项玛,著一層夾襖步出監(jiān)牢的瞬間貌笨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工襟沮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锥惋,地道東北人昌腰。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像膀跌,于是被迫代替她去往敵國和親遭商。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360