我們在學習 HashMap 的時候,都知道 HashMap 是非線程安全的,同時我們知道 HashTable 是線程安全的,因為里面的方法使用了 synchronized 進行同步。
但是 HashMap 為什么是非線程安全的呢?難道僅僅就是因為內部的方法沒有 synchronized 關鍵字修飾嗎?這篇文章主要來分析一下原因。
我們知道 HashMap 底層是一個 Entry 數組,當發生 hash 沖突的時候,HashMap 是采用鏈表的方式來解決的,在對應的數組位置存放鏈表的頭結點。對鏈表而言,新加入的節點會從頭結點加入。
1、HashMap 在插入的時候
現在假如 A 線程和 B 線程同時進行插入操作,然后計算出了相同的哈希值對應了相同的數組位置,因為此時該位置還沒數據,然后對同一個數組位置,兩個線程會同時得到現在的頭結點,然后 A 寫入新的頭結點之后,B 也寫入新的頭結點,那B的寫入操作就會覆蓋 A 的寫入操作造成 A 的寫入操作丟失。
2、HashMap 在擴容的時候
HashMap 有個擴容的操作,這個操作會新生成一個新的容量的數組,然后對原數組的所有鍵值對重新進行計算和寫入新的數組,之后指向新生成的數組。
那么問題來了,當多個線程同時進來,檢測到總數量超過門限值的時候就會同時調用 resize 操作,各自生成新的數組並 rehash 后賦給該 map 底層的數組,結果最終只有最后一個線程生成的新數組被賦給該 map 底層,其他線程的均會丟失。
3、HashMap 在刪除數據的時候
刪除這一塊可能會出現兩種線程安全問題,第一種是一個線程判斷得到了指定的數組位置i並進入了循環,此時,另一個線程也在同樣的位置已經刪掉了i位置的那個數據了,然后第一個線程那邊就沒了。但是刪除的話,沒了倒問題不大。
再看另一種情況,當多個線程同時操作同一個數組位置的時候,也都會先取得現在狀態下該位置存儲的頭結點,然后各自去進行計算操作,之后再把結果寫會到該數組位置去,其實寫回的時候可能其他的線程已經就把這個位置給修改過了,就會覆蓋其他線程的修改。
其他地方還有很多可能會出現線程安全問題,我就不一一列舉了,總之 HashMap 是非線程安全的,有並發問題時,建議使用 ConcrrentHashMap。