HashMap多執行緒下發生死迴圈的原因

Sam同學發表於2019-03-02

概述


大神陳皓已經在疫苗:JAVA HASHMAP的死迴圈一文中詳細描述了HashMap多執行緒下產生死迴圈的原因,我仔細研讀了這篇大作,做了一些筆記,加上自己的一些理解
整理出一些資訊,發出來與大家交流交流。


HashMap儲存的資料結構


陳皓在Hash表資料結構這一節提到了HashMap的資料結構以及擴容問題,關於這一點我之前寫過的
HashMap的put和get方法原理HashMap擴容已經有詳細的描述了。


多執行緒rehash的時候如何造成閉環連結串列


rehash原始碼

這裡寫圖片描述
這裡寫圖片描述

這裡寫圖片描述
這裡寫圖片描述


正常的rehash過程


資料準備
在size=2的HashMap中按照順序新增5, 7, 3這三個key,假設按照mod 2的演算法來計算元素陣列下標,那麼key 5,7,3都會落在下標為1的陣列桶中(發生hash衝突),如下圖:

這裡寫圖片描述
這裡寫圖片描述

把HashMap的size擴容為4後,rehash的過程

注意,發生hash衝突的5,7,3雖然都是在同一個連結串列中,但是每個元素都得走rehash的過程,因為HashMap擴容後,這幾個元素就未必都是在同一個連結串列中了

1、第一個是處理3這個key,先把key為3這個元素的next設定為空,並計算它在新陣列中的下標,並存到新下標對應桶中,如下圖:

這裡寫圖片描述
這裡寫圖片描述

2、第二個是處理7這個key,按照上面的約定,在新陣列中3和7這個兩個key還是發生了hash衝突,那麼按照HashMap發生衝突的處理程式碼,連結串列的第一個元素儲存的是最新插入的7,然後next指向3,如下圖:

這裡寫圖片描述
這裡寫圖片描述

3、第三個是處理5這個key,如下圖:

這裡寫圖片描述
這裡寫圖片描述

到這裡一次正常rehash過程走完了,最後三個key的儲存情況如下圖:

這裡寫圖片描述
這裡寫圖片描述


併發下的rehash過程


當兩個併發執行緒thread1和thread2都同時進入到transfer時,也即是,剛好thread1和thread2都要對HashMap進行擴容,萬一這個時候thread1執行下面的程式碼時,被執行緒排程器掛起了,而thread2則正常的把擴容的操作做完,如下圖:

這裡寫圖片描述
這裡寫圖片描述

那這個時候,容器的資料儲存情況如下:
對於thread1

這裡寫圖片描述
這裡寫圖片描述

對於thread2

這裡寫圖片描述
這裡寫圖片描述

這個時候,thread1擁有執行許可權了,則繼續它的擴容操作,等thread1擴容完後就產生了一個環形連結串列了(注意這裡省略了一些步驟,不太明白的,則可以看我之前寫的HashMap的put和get方法原理HashMap擴容)

這裡寫圖片描述
這裡寫圖片描述

這個時候,如果有個get請求,就有可能發生死迴圈,一直在連結串列中繞來繞去的,沒法終止。


原文連結


HashMap多執行緒下發生死迴圈的原因

相關文章