圖解JDK7中的HashMap閉環和丟失問題

關於HashMap的線程安全問題,網上資料很多。對於1.7版本的閉環問題,看了很多網上的資料一直搞不懂,今天又琢磨了一下,終於明白了,用自己看得懂的方式記錄下。

首先說下閉環產生的原因:1.7的HashMap在擴容複製時,採用的是頭插入法,這會導致原數組中的鏈表反轉,即將原來的正向,複製成反向鏈表。
而在多線程環境下,可能存在其他線程完成了擴容複製操作,完成了後的數組鏈表變成了原來的反向鏈表。
由於可見性的保證,當前線程繼續複製時,複製的時其他線程已經完成了的反向鏈表,但當前線程又會將其反轉,導致又變成了正向鏈表,那麼在當前線程複製過程中會既有正向的鏈表又有反向的鏈表,從而可能產生閉環的情況。
具體的圖例如下
左邊的爲擴容前的分佈,右邊的爲正常單線程情況下完成複製後的情況。
在這裏插入圖片描述
首先說下頭插入的過程,首先遍歷舊數組鏈表,拿到第一個元素A,保存其後續鏈表B,然後計算在新數組位置1並拿到首節點NULL,將A的next設置爲NULL
在這裏插入圖片描述
將A放入新數組對應位置中
在這裏插入圖片描述
將原後續鏈表續到原數組中
在這裏插入圖片描述
以上是正常的移動過程,現在分析併發場景下產生的閉環問題和丟失問題

閉環問題

  1. 線程1執行到這裏後掛起
    在這裏插入圖片描述
  2. 線程2完成複製工作如下如
    在這裏插入圖片描述
  3. 線程1喚醒,繼續操作,由於可見性,老數組中的B的next實際上已經變成了A,線程1此時會將A至於新數組索引1中,即虛線框會發送覆蓋
    在這裏插入圖片描述
  4. 覆蓋後變成下圖
    在這裏插入圖片描述
  5. 繼續完成B的複製,如下圖
    在這裏插入圖片描述
  6. 舊數組中還剩下一個A,繼續。還記得前面移動的步驟,首先會變更A的next指向B,然後放入新數組中。
    在這裏插入圖片描述
  7. 最終會成爲下圖形成閉環
    在這裏插入圖片描述

丟失

  1. 線程1在此處掛起
    在這裏插入圖片描述
  2. 線程2完成複製,如下圖
    在這裏插入圖片描述
  3. 線程1甦醒,完成A的複製,此時B的next已經變成了NULL,C丟失
    在這裏插入圖片描述
  4. 複製B,C沒了
    在這裏插入圖片描述

1.8尾插入發

由上面可知,丟失和閉環均是由於改變鏈表方向導致的,設計者一開始使用頭插入法是考慮碰撞時後加入的數據訪問的可能性更大,頭插入有更好的查找性能。
1.8改爲尾插入,不會進行鏈表方向的改變,因此不會發生上面兩種情況

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