jdk1 7中HashMap死迴圈分析

2021-10-08 16:54:08 字數 1973 閱讀 4452

在多執行緒環境下,使用hashmap進行put操作會引起死迴圈,導致cpu利用率接近100%,hashmap在併發執行put操作時會引起死迴圈,是因為多執行緒會導致hashmap的entry鍊錶

形成環形資料結構,一旦形成環形資料結構,entry的next節點永遠不為空,就會產生死迴圈獲取entry。那麼這個死迴圈是如何生成的呢?我們來仔細分析下。

hashmap擴容流程原理

引發死迴圈,是在hashmap的擴容操作中。

正常的擴容操作是這個流程。hashmap的擴容在put操作中會觸發擴容,主要是三個方法:

綜合來說,hashmap一次擴容的過程:

1、取當前table的2倍作為新table的大小

2、根據算出的新table的大小new出乙個新的entry陣列來,名為newtable

3、輪詢原table的每乙個位置,將每個位置上連線的entry,算出在新table上的位置,並以鍊錶形式連線

4、原table上的所有entry全部輪詢完畢之後,意味著原table上面的所有entry已經移到了新的table上,hashmap中的table指向newtable例項

現在hashmap中有三個元素,hash表的size=2, 所以key = 3, 7, 5,在mod 2以後都衝突在table[1]這裡了。

按照方法中的**

對table[1]中的鍊錶來說,進入while迴圈,此時e=key(3),那麼next=key(7),經過計算重新定位e=key(3)在新錶中的位置,並把e=key(3)掛在newtable[3]的位置

這樣迴圈下去,將table[1]中的鍊錶迴圈完成後,於是hashmap就完成了擴容

併發下的擴容

上面都是單執行緒下的擴容,當多執行緒進行擴容時,會是什麼樣子呢?

初始的hashm還是:

我們現在假設有兩個執行緒併發操作,都進入了擴容操作,

回顧我們的擴容**,我們假設,執行緒1執行到entrynext = e.next;時被作業系統排程掛起了,而執行緒2執行完成了擴容操作

接下來,執行緒1被排程回來執行:

1)2)

3)4)

5)6)

7)迴圈列表產生後,一旦執行緒1呼叫get(11,15之類的元素)時,就會進入乙個死迴圈的情況,將cpu的消耗到100%。

總結hashmap之所以在併發下的擴容造成死迴圈,是因為,多個執行緒併發進行時,因為乙個執行緒先期完成了擴容,將原map的鍊錶重新雜湊到自己的表中,並且鍊錶變成了倒序,後乙個執行緒再擴容時,又進行自己的雜湊,再次將倒序鍊錶變為正序鍊錶。於是形成了乙個環形鍊錶,當get表中不存在的元素時,造成死迴圈。

JDK1 8中的hashmap和JDK1 7的區別

1.資料插入的方式不同 jdk1.7用的是頭插法,而jdk1.8用的是尾插法,這是由於jdk1.7是用單鏈表進行的縱向延伸,當採用頭插法時會容易出現逆序且環形鍊錶死迴圈問題。但是在jdk1.8之後是因為加入了紅黑樹使用尾插法,能夠避免出現逆序且鍊錶死迴圈的問題。2.組成結構不同 jdk1.7的時候使...

JDK1 7的HashMap死迴圈

為什麼在jdk1.7多執行緒情況下會很容易出現hashmap死迴圈,這個還是要根據它採取的擴容策略來看,它的擴容策略是頭插法,因此會導致這樣的問題。在jdk1.8改進為尾插法,但並不意味著尾插法能適應多執行緒併發的場景,我認為其最主要的考慮就是頭插法在正常情況下是與原來鍊錶順序相逆的,而尾插不會改變...

JDK1 7的HashMap原始碼解讀

default initial capacity 初始化容量,中為1 4 即為16。為什麼要這樣寫呢?maximum capacity 最大容量,中衛1 30 即為2的30次冪。30次冪的原因是 改屬性為int型別,int型別最大為4個位元組,共32個二進位制位,理論上可以向左移動31次,即31次冪...