ConcurrentHashMap的一個bug

最近發(fā)現(xiàn)java 1.8的concurrentHashMap甸赃,在使用computeIfAbsent時,如果涉及修改map趟据,則會產(chǎn)生bug券犁。
示例代碼如下:

        System.out.println("start.");
        map.computeIfAbsent("t",
                (String t) -> map.computeIfAbsent("t", (String i) -> "i")); //halt在這里
        System.out.println("fin.");

如果執(zhí)行這段代碼,你會發(fā)現(xiàn)代碼會停在注釋出汹碱,一直沒有結(jié)果粘衬。
最開始以為是遞歸實現(xiàn)的問題,通俗的說咳促,就是在構造一個函數(shù)的時候陷入了自遞歸稚新。就是你想構造一個A,但是A的構造依賴A已完成構造后的某些屬性跪腹。為了驗證是否是這個原因褂删,我們把代碼做一些調(diào)整,消除遞歸調(diào)用冲茸。

        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        System.out.println("start.");
        map.computeIfAbsent("t",
                (String t) -> {
                    map.put("t", "t");
                    return "t";
                });
        System.out.println("fin.");

你會發(fā)現(xiàn)屯阀,代碼繼續(xù)停在哪里缅帘,無法輸出"fin."。

然后懷疑是死鎖难衰,懷疑concurrentHashMap使用了非可重入鎖钦无。但是跟著看conrrentHashMap的實現(xiàn),發(fā)現(xiàn)是基于cas + synchronized的方式實現(xiàn)盖袭,而synchronized本身是可重入的失暂,因此這里不滿足死鎖的條件。
繼續(xù)看concurrentHashMap的注釋苍凛,里面有這樣一句話:

/*
  must not attempt to update any other mappings of this map.
*/

這句話確定了這個問題應該是已知存在的趣席。
所以應該絕對避免在computeIfAbsent中有遞歸,或者修改map的任何操作醇蝴。

為了搞清楚原因宣肚,我們繼續(xù)debug concurrentHashMap的源碼,發(fā)現(xiàn)這種在computeIfAbsent中悠栓,如果嘗試修改map的情況下霉涨,代碼會在

        for (Node<K,V>[] tab = table;;) {  //無限循環(huán)
            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) & h)) == null) {
                Node<K,V> r = new ReservationNode<K,V>();
                synchronized (r) {
                    if (casTabAt(tab, i, null, r)) { //cas
....

中反復循環(huán)。
我嘗試通俗的解釋一下這個問題:
注:不見得正確惭适,只是個人理解
由于concurrentHashMap中使用的是cas操作笙瑟,因此在出現(xiàn)cas嵌套的情況下,就會形成一種『死鎖』癞志。舉例來說往枷,一個值原來是 1, 我想把它修改成2凄杯,正常的cas操作错洁,會比較在修改的那一刻,值是否仍然為1戒突。這種比較屯碴,在cas只有一層的情況下,是沒有問題的膊存。但是导而,假如有兩層cas,這個值原來是1隔崎,第一層把 1 -> 2今艺,在cas還沒有生效時,繼續(xù)進入第二層cas操作爵卒,把 2 -> 3虚缎,當最終提交時,第二層cas比較當前值是否是2技潘,但由于當前指仍然是1遥巴,因此修改無效。最終反復進入循環(huán)享幽,形成死鎖铲掐。

雖然computeIfAbsent的代碼注釋中對這種修改map的行為做了強提示,但在實際中值桩,我認為這種行為仍舊是concurrentHashMap的一個實現(xiàn)bug摆霉。

https://bugs.openjdk.java.net/browse/JDK-8172951
好在這個問題在java 1.9中已經(jīng)基本修復了。

This is fixed in JDK 9 with JDK-8071667 . When the test case is run in JDK 9-ea, it gives a ConcurrentModification Exception.
java 9

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奔坟,一起剝皮案震驚了整個濱河市携栋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咳秉,老刑警劉巖婉支,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異澜建,居然都是意外死亡向挖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門炕舵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來何之,“玉大人,你說我怎么就攤上這事咽筋∪芡疲” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵奸攻,是天一觀的道長蒜危。 經(jīng)常有香客問我,道長舞箍,這世上最難降的妖魔是什么舰褪? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮疏橄,結(jié)果婚禮上占拍,老公的妹妹穿的比我還像新娘。我一直安慰自己捎迫,他們只是感情好晃酒,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窄绒,像睡著了一般贝次。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彰导,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天蛔翅,我揣著相機與錄音敲茄,去河邊找鬼。 笑死山析,一個胖子當著我的面吹牛堰燎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笋轨,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼秆剪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了爵政?” 一聲冷哼從身側(cè)響起仅讽,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钾挟,沒想到半個月后洁灵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡掺出,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年处渣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛛砰。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡罐栈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泥畅,到底是詐尸還是另有隱情荠诬,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布位仁,位于F島的核電站柑贞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏聂抢。R本人自食惡果不足惜钧嘶,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琳疏。 院中可真熱鬧有决,春花似錦、人聲如沸空盼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽揽趾。三九已至台汇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苟呐。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工痒芝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人牵素。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓吼野,卻偏偏與公主長得像,于是被迫代替她去往敵國和親两波。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

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