關於HashMap 併發時會引起死迴圈的問題解析

2022-09-26 18:21:14 字數 1877 閱讀 8857

今天研讀j**a併發容器和框架時,看到為什麼要使用concurrenthashmap時,其中有乙個原因是:執行緒不安全的hashmap, hashmap在併發執行put操作時會引起死迴圈,是因為多執行緒會導致hashmap的entry鍊錶形成環形資料結構,查詢時會陷入死迴圈。

糾起原因看了其他的部落格,都比較抽象,所以這裡以圖形的方式展示一下,希望支援!

1)當往hashmap中新增元素時,會引起hashmap容器的擴容,原理不再解釋,直接附源**,如下:

/**

* * 往表中新增元素,如果插入元素之後,表長度不夠,便會呼叫resize方法擴容

*/

void addentry(int hash, k key, v int bucketindex)

/**

* resize()方法如下,重要的是transfer方法,把舊表中的元素新增到新錶中

*/

void resize(int newcapacity)

entry newtable = new entry[newcapacity];

transfer(newtable);

table = newtable;

threshold = (int)(newcapacity * loadfactor);

} 2)參考上面的**,便引入到了transfer方法,(引入重點)這就是hashmap併發時,會引起死迴圈的根本原因所在,下面結合transfer的源**,說明一下產生死迴圈的原理,先列transfer**(這是裡jdk7的源偌),如下:

/*** transfers all entries from current table to newtable.

*/void transfer(entry newtable, boolean rehash)

int i = indexfor(e.hash, newcapacity);

e.next = newtable[i];

newtable[i] = e;

e = next;

} // while}}

3)假設:

map map = new hashmap(2); // 只能放置兩個元素,其中的threshold為1(表中只填充乙個元素時),即插入元素為1時就擴容(由addentry方法中得知)

//放置2個元素 3 和 7,若要再放置元素8(經hash對映後不等於1)時,會引起擴容

假設放置結果圖如下:

現在有兩個執行緒a和b,都要執行put操作,即向表中新增元素,即執行緒a和執行緒b都會看到上面圖的狀態快照

執行順序如下:

執行一: 執行緒a執行到transfer函式中(1)處掛起(transfer函式**中有標註)。此時**程a的棧中

e = 3

next = 7

執行二:執行緒b執行 transfer函式中的while迴圈,即會把原來的table變成新一table(執行緒b自己的棧中),再寫入到記憶體中。如下圖(假設兩個元素在新的hash函式下也會對映到同乙個位置)

執行三:執行緒a解掛,接著執行(看到的仍是舊表),即從transfer** 1)處接著執行,當前的 e = 3, next = 7, 上面已經描述。

1.處理元素 3 , 將 3 放入 執行緒a自己棧的新table中(新table是處於執行緒a自己棧中,是執行緒私有的,不肥執行緒2的影響),處理3後的圖如下:

2.執行緒a再複製元素 7 ,當前 e = 7 ,而next值由於執行緒 b 修改了它的引用,所以next 為 3 ,處理後的新錶如下圖

3.由於上面取到的next = 3, 接著while迴圈,即當前處理的結點為3, next就為null ,退出while迴圈,執行完while迴圈後,新錶中的內容如下圖:

4.當操作完成,執行查詢時,會陷入死迴圈!

原文鏈結:

HashMap基礎與併發

執行緒不安全的hashmap,hashmap在併發執行put操作時會引起死迴圈,是因為多執行緒會導致hashmap的entry鍊錶形成環形資料結構,查詢時會陷入死迴圈。預設初始化大小為16,之後每次擴充,容量變為原來的2倍 預設載入因子為0.75 modcount作用 迭代器每修改一次就 1 has...

高併發下的HashMap

1.hashmap在插入元素過多的時候需要進行resize,resize的條件是 hashmap.size capacity loadfactor。2.hashmap的resize包含擴容和rehash兩個步驟,rehash在併發的情況下可能會形成鍊錶環 hashmap進行儲存時,假設size超過當...

《併發程式設計》 15 執行緒安全的HashMap

眾所周知,hashmap是執行緒不安全的。但是如果需要乙個執行緒按鈕的hashmap我們需要怎麼做的。其中乙個可行的辦法就是collections.synchronizedmap。如下 就是執行緒安全的hashmap public static map m collections.synchroni...