为什么说HashMap是线程不安全的?
生活随笔
收集整理的這篇文章主要介紹了
为什么说HashMap是线程不安全的?
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、頭插法導致死循環
在jdk1.7以前,HashMap在進行擴容時采用的是頭插法,可能當時別人覺得這樣比較高效,但是也帶來了線程安全問題。
剛開始時HashMap是這樣的:
正常擴容后是這樣的:
但如果是在多線程下,兩個線程的指向3:
此時線程1比線程2先執行,那么線程1就會指向7,將線程7.next指向了3,:
但是對于線程2來說,3.next=7;所以就形成了死循環,也就是3和7構成了環。
二、數據覆蓋
在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方法,會出現線程不安全的代碼起源我已經標出。比如現在有兩個線程都要調用put方法,都進行了判斷,且都滿足條件可以直接插入,這時線程1先插入,線程2在執行的時候就不會再次進行判斷,也是直接插入,這就出現了元素覆蓋,也就是說線程1做了無用功。
三、線程安全的字典
那么HashMap是線程不安全的,我們在多線程的場景下可以使用線程安全的字典:
3.1Hashtable
這個類相當于是在主要的方法前加了synchronized修飾,所以效率會非常低,通常不推薦使用
3.2ConcurrentHashMap
ConcurrentHashMap減小了鎖的離度,在鏈表的頭結點加鎖,效率相對高一些。
總結
以上是生活随笔為你收集整理的为什么说HashMap是线程不安全的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AFG与AWG的比较
- 下一篇: 90后alia炫富女