由於在公司專案中偶爾會遇到hashmap死迴圈造成cpu100%,重啟後問題消失,隔一段時間又會反覆出現。今天在這裡來仔細剖析下多執行緒情況下hashmap所帶來的問題:
1、多執行緒put操作後,get操作導致死迴圈。
2、多執行緒put非null元素後,get操作得到null值。
3、多執行緒put操作,導致元素丟失。
下面我用一段簡單的demo模擬hashmap死迴圈:
1其中map和at都是static的,即所有執行緒所共享的資源。接著5個執行緒併發操作該hashmap:public
class test extends
thread
213 }
1反覆執行幾次,出現這種情況則表示死迴圈了:public
static
void
main(string args)
2
接下來我們去檢視下cpu以及堆疊情況:
通過堆疊可以看到:thread-3由於hashmap的擴容操作導致了死迴圈。
我們先來看下單執行緒情況下,正常的rehash過程
1、假設我們的hash演算法是簡單的key mod一下表的大小(即陣列的長度)。
2、最上面是old hash表,其中hash表的size=2,所以key=3,5,7在mod 2 以後都衝突在table[1]這個位置上了。
3、接下來hash表擴容,resize=4,然後所有的重新進行雜湊分布,過程如下:
在單執行緒情況下,一切看起來都很美妙,擴容過程也相當順利。接下來看下併發情況下的擴容。
1、首先假設我們有兩個執行緒,分別用紅色和藍色標註了。
2、擴容部分的源**:
4、接著cpu切換到執行緒一上來,執行8-14行**,首先安置3這個entry:
這裡需要注意的是:執行緒二已經完成執行完成,現在table裡面所有的entry都是最新的,就是說7的next是3,3的next是null;現在第一次迴圈已經結束,3已經安置妥當。看看接下來會發生什麼事情:
1、e=next=7;
2、e!=null,迴圈繼續
3、next=e.next=3
4、e.next 7的next指向3
5、放置7這個entry,現在如圖所示:
放置7之後,接著執行**:
1、e=next=3;
2、判斷不為空,繼續迴圈
3、next= e.next 這裡也就是3的next 為null
4、e.next=7,就3的next指向7.
5、放置3這個entry,此時的狀態如圖:
這個時候其實就出現了死迴圈了,3移動節點頭的位置,指向7這個entry;在這之前7的next同時也指向了3這個entry。
**接著往下執行,e=next=null,此時條件判斷會終止迴圈。這次擴容結束了。但是後續如果有查詢(無論是查詢的迭代還是擴容),都會hang死在table這個位置上。現在回過來看文章開頭的那個demo,就是掛死在擴容階段的transfer這個方法上面。
出現上面這種情況絕不是我要在測試環境弄一批資料專門為了演示這種問題。我們仔細思考一下就會得出這樣乙個結論:如果擴容前相鄰的兩個entry在擴容後還是分配到相同的table位置上,就會出現死迴圈的bug。在複雜的生產環境中,這種情況儘管不常見,但是可能會碰到。
下面來介紹下元素丟失的問題。這次我們選取3、5、7的順序來演示:
2、執行緒二執行完成:
3、這個時候接著執行執行緒一,首先放置7這個entry:
4、再放置5這個entry:
5、由於5的next為null,此時擴容動作結束,導致3這個entry丟失。
這個問題當初有人上報到sun公司,不過sun不認為這是乙個問題。因為hashmap本來就不支援併發。
如果大家想在併發場景下使用hashmap,有兩種解決方法:
1、使用concurrenthashmap。
2、使用collections.synchronizedmap(maom)方法把hashmap變成乙個執行緒安全的map。
P說 多執行緒HashMap產生死迴圈
在1.7及以前,使用hashmap進行put操作時,當元素超過閾值時,會觸發resize操作,這時候可能就會出現死迴圈的情況,導致cpu佔用率達到100 首先,我們要先了解hashmap的資料結構,hashmap的主幹是乙個entry陣列。entry是hashmap的基本組成單元,每乙個entry包...
HashMap在java併發中如何發生死迴圈
在多執行緒環境中,使用hashmap進行put操作時會引起死迴圈,導致cpu使用接近100 下面通過 分析一下為什麼會發生死迴圈。static class entryimplements map.entry the table,resized as necessary.length must alw...
多執行緒 happens before 先行發生原則
這8條規則中,前4條規則是比較重要的,後4條規則都是顯而易見的。下面我們來解釋一下前4條規則 對於程式次序規則來說,我的理解就是一段程式 的執行在單個執行緒中看起來是有序的。注意,雖然這條規則中提到 書寫在前面的操作先行發生於書寫在後面的操作 這個應該是程式看起來執行的順序是按照 順序執行的,因為虛...