hashmap是非執行緒安全的,表現在兩種情況下:
1 擴容:
t1執行緒對map進行擴容,此時t2執行緒來讀取資料,原本要讀取位置為2的元素,擴容後此元素位置未必是2,則出現讀取錯誤資料。
2 hash碰撞
兩個執行緒新增元素發生hash碰撞,都要將此元素新增到鍊錶的頭部,則會發生資料被覆蓋。
詳情:
hashmap底層是乙個node陣列,一旦發生hash衝突的的時候,hashmap採用拉鍊法解決碰撞衝突,node結構:
/*** basic hash bin node, used for most entries. (see below for
* treenode subclass, and in linkedhashmap for its entry subclass.)
*/static
class nodeimplements map.entry
public
final k getkey()
public
final v getvalue()
public
final string tostring()
public
final
inthashcode()
public
final
v setvalue(v newvalue)
public
final
boolean
equals(object o)
return
false
; }
}
node的變數:
finalinthash;
final
k key;
v value;
node
next;
執行put方法新增元素後呼叫此方法:
/*** implements map.put and related methods**
@param
hash hash for key
* @param
key the key
* @param
value the value to put
* @param
onlyifabsent if true, don't change existing value
* @param
evict if false, the table is in creation mode.
* @return
previous value, or null if none
*/final v putval(int hash, k key, v value, boolean
onlyifabsent,
boolean
evict)
//如果發現相同的key,同樣覆蓋
if (e.hash == hash &&((k = e.key) == key || (key != null &&key.equals(k))))
break
; p =e;}}
if (e != null)
}++modcount;
if (++size >threshold)
resize();
afternodeinsertion(evict);
return
null
; }
在併發情況下,新增元素,在 1位置 會出現執行緒安全問題
擴容問題:
/*** initializes or doubles table size. if null, allocates in
* accord with initial capacity target held in field threshold.
* otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.**
@return
the table
*/final node resize()
//如果當前長度*2 小於最大值,並且當前長度大於等於預設長度16,閾值*2
else
if ((newcap = oldcap << 1) < maximum_capacity &&oldcap >=default_initial_capacity)
newthr = oldthr << 1; //
double threshold
}
//如果閾值大於0,則將新陣列的長度設定為閾值=16
else
if (oldthr > 0) //
initial capacity was placed in threshold
newcap =oldthr;
//零初始閾值表示使用預設值
else
if (newthr == 0)
threshold =newthr;
@suppresswarnings()
//建立新的陣列
node newtab = (node)new
node[newcap];
table =newtab;
//將舊資料遷移到新陣列
if (oldtab != null
)
else
} while ((e = next) != null
);
if (lotail != null
)
if (hitail != null
) }}}
}return
newtab;
}
執行緒1在取資料時,map被其它執行緒擴容,則造成取到錯誤資料
HashMap 執行緒不安全的原因
hashmap執行緒安全的問題,在各大面試中都會被問到,屬於常考熱點題目。雖然大部分讀者都了解它不是執行緒安全的,但是再深入一些,問它為什麼不是執行緒安全的,仔細說說原理,用圖畫出一種非執行緒安全的情況?1.8之後又做了什麼改善了這點?很多讀者可能一時想不出很好的答案。我們關注下面的 void tr...
談談HashMap執行緒不安全的體現
hashmap的原理以及如何實現,之前在 jdk7與jdk8中hashmap的實現 中已經說明了。那麼,為什麼說hashmap是執行緒不安全的呢?它在多執行緒環境下,會發生什麼情況呢?1.resize死迴圈 我們都知道hashmap初始容量大小為16,一般來說,當有資料要插入時,都會檢查容量有沒有超...
hashmap的執行緒不安全性
首先hashmap在多個執行緒同時對其操作的時候造成的髒讀很統一理解,比如乙個執行緒a對hashmap進行讀操作,乙個執行緒b對hashmap就行寫操作。執行緒b先進入put方法中,此時還沒有寫資料的時候執行緒a輪轉執行,並一直執行到結束,假設執行取到資料為條,這時執行緒b繼續執行新增了一條資料。那...