日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

漫画:高并发下的HashMap

發布時間:2023/12/3 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 漫画:高并发下的HashMap 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自?玻璃貓 程序員小灰

上一期我們介紹了HashMap的基本原理,

這一期我們來講解高并發環境下,HashMap可能出現的致命問題。













HashMap的容量是有限的。當經過多次元素插入,使得HashMap達到一定飽和度時,Key映射位置發生沖突的幾率會逐漸提高。


這時候,HashMap需要擴展它的長度,也就是進行Resize





影響發生Resize的因素有兩個:


1.Capacity

HashMap的當前長度。上一期曾經說過,HashMap的長度是2的冪。


2.LoadFactor

HashMap負載因子,默認值為0.75f。



衡量HashMap是否進行Resize的條件如下:


HashMap.Size? ?>=? Capacity * LoadFactor







1.擴容

創建一個新的Entry空數組,長度是原數組的2倍。


2.ReHash

遍歷原Entry數組,把所有的Entry重新Hash到新數組。為什么要重新Hash呢?因為長度擴大以后,Hash的規則也隨之改變。


讓我們回顧一下Hash公式:

index =? HashCode(Key) &? (Length?- 1)?


當原數組長度為8時,Hash運算是和111B做與運算;新數組長度為16,Hash運算是和1111B做與運算。Hash結果顯然不同。


Resize前的HashMap:




Resize后的HashMap:




ReHash的Java代碼如下:


[java]?view plaincopy
  • /**?
  • ?*?Transfers?all?entries?from?current?table?to?newTable.?
  • ?*/??
  • void?transfer(Entry[]?newTable,?boolean?rehash)?{??
  • ????int?newCapacity?=?newTable.length;??
  • ????for?(Entry<K,V>?e?:?table)?{??
  • ????????while(null?!=?e)?{??
  • ????????????Entry<K,V>?next?=?e.next;??
  • ????????????if?(rehash)?{??
  • ????????????????e.hash?=?null?==?e.key???0?:?hash(e.key);??
  • ????????????}??
  • ????????????int?i?=?indexFor(e.hash,?newCapacity);??
  • ????????????e.next?=?newTable[i];??
  • ????????????newTable[i]?=?e;??
  • ????????????e?=?next;??
  • ????????}??
  • ????}??
  • }??








  • 注意:下面的內容十分燒腦,請小伙伴們坐穩扶好。


    假設一個HashMap已經到了Resize的臨界點。此時有兩個線程A和B,在同一時刻對HashMap進行Put操作:







    此時達到Resize條件,兩個線程各自進行Rezie的第一步,也就是擴容:




    這時候,兩個線程都走到了ReHash的步驟。讓我們回顧一下ReHash的代碼:



    假如此時線程B遍歷到Entry3對象,剛執行完紅框里的這行代碼,線程就被掛起。對于線程B來說:


    e = Entry3

    next = Entry2


    這時候線程A暢通無阻地進行著Rehash,當ReHash完成后,結果如下(圖中的e和next,代表線程B的兩個引用):




    直到這一步,看起來沒什么毛病。接下來線程B恢復,繼續執行屬于它自己的ReHash。線程B剛才的狀態是:

    e = Entry3

    next = Entry2

    當執行到上面這一行時,顯然 i = 3,因為剛才線程A對于Entry3的hash結果也是3。



    我們繼續執行到這兩行,Entry3放入了線程B的數組下標為3的位置,并且e指向了Entry2。此時e和next的指向如下:


    e = Entry2

    next = Entry2


    整體情況如圖所示:





    接著是新一輪循環,又執行到紅框內的代碼行:



    e = Entry2

    next = Entry3


    整體情況如圖所示:




    接下來執行下面的三行,用頭插法把Entry2插入到了線程B的數組的頭結點:



    整體情況如圖所示:




    第三次循環開始,又執行到紅框的代碼:



    e = Entry3

    next = Entry3.next = null


    最后一步,當我們執行下面這一行的時候,見證奇跡的時刻來臨了:


    newTable[i] =?Entry2

    e = Entry3

    Entry2.next = Entry3

    Entry3.next = Entry2


    鏈表出現了環形!


    整體情況如圖所示:





    此時,問題還沒有直接產生。當調用Get查找一個不存在的Key,而這個Key的Hash結果恰好等于3的時候,由于位置3帶有環形鏈表,所以程序將會進入死循環











    1.Hashmap在插入元素過多的時候需要進行Resize,Resize的條件是

    HashMap.Size? ?>=? Capacity * LoadFactor。


    2.Hashmap的Resize包含擴容和ReHash兩個步驟,ReHash在并發的情況下可能會形成鏈表環。


    總結

    以上是生活随笔為你收集整理的漫画:高并发下的HashMap的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。