为什么说HashMap是线程不安全的?
一、頭插法導(dǎo)致死循環(huán)
在jdk1.7以前,HashMap在進(jìn)行擴(kuò)容時(shí)采用的是頭插法,可能當(dāng)時(shí)別人覺得這樣比較高效,但是也帶來了線程安全問題。
剛開始時(shí)HashMap是這樣的:
正常擴(kuò)容后是這樣的:
但如果是在多線程下,兩個(gè)線程的指向3:
此時(shí)線程1比線程2先執(zhí)行,那么線程1就會(huì)指向7,將線程7.next指向了3,:
但是對(duì)于線程2來說,3.next=7;所以就形成了死循環(huán),也就是3和7構(gòu)成了環(huán)。
二、數(shù)據(jù)覆蓋
在jdk1.8以后,改了1.7以前的小毛病,但是新的問題又來了,我們來看下源碼:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//問題出在這里if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}這是HashMap的put方法,會(huì)出現(xiàn)線程不安全的代碼起源我已經(jīng)標(biāo)出。比如現(xiàn)在有兩個(gè)線程都要調(diào)用put方法,都進(jìn)行了判斷,且都滿足條件可以直接插入,這時(shí)線程1先插入,線程2在執(zhí)行的時(shí)候就不會(huì)再次進(jìn)行判斷,也是直接插入,這就出現(xiàn)了元素覆蓋,也就是說線程1做了無用功。
三、線程安全的字典
那么HashMap是線程不安全的,我們?cè)诙嗑€程的場(chǎng)景下可以使用線程安全的字典:
3.1Hashtable
這個(gè)類相當(dāng)于是在主要的方法前加了synchronized修飾,所以效率會(huì)非常低,通常不推薦使用
3.2ConcurrentHashMap
ConcurrentHashMap減小了鎖的離度,在鏈表的頭結(jié)點(diǎn)加鎖,效率相對(duì)高一些。
總結(jié)
以上是生活随笔為你收集整理的为什么说HashMap是线程不安全的?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AFG与AWG的比较
- 下一篇: 90后alia炫富女