并发的HashMap为什么会引起死循环?(转)
本文轉自http://blog.csdn.net/zhuqiuhui/article/details/51849692
今天研讀Java并發容器和框架時,看到為什么要使用ConcurrentHashMap時,其中有一個原因是:線程不安全的HashMap, HashMap在并發執行put操作時會引起死循環,是因為多線程會導致HashMap的Entry鏈表形成環形數據結構,查找時會陷入死循環。糾起原因看了其他的博客,都比較抽象,所以這里以圖形的方式展示一下,希望支持!
?
(1)當往HashMap中添加元素時,會引起HashMap容器的擴容,原理不再解釋,直接附源代碼,如下:
[java]?view plaincopy(2)參考上面的代碼,便引入到了transfer方法,(引入重點)這就是HashMap并發時,會引起死循環的根本原因所在,下面結合transfer的源代碼,說明一下產生死循環的原理,先列transfer代碼(這是里JDK7的源偌),如下:
[java]?view plaincopy?
?
(3)假設:
[java]?view plaincopy假設放置結果圖如下:
? ? ?
?現在有兩個線程A和B,都要執行put操作,即向表中添加元素,即線程A和線程B都會看到上面圖的狀態快照
執行順序如下:
? ? ? ? ? ? ? ?執行一: ?線程A執行到transfer函數中(1)處掛起(transfer函數代碼中有標注)。此時在線程A的棧中
?
[java]?view plaincopy?
?
? ? ? ? ? ? ? 執行二:線程B執行 transfer函數中的while循環,即會把原來的table變成新一table(線程B自己的棧中),再寫入到內存中。如下圖(假設兩個元素在新的hash函數下也會映射到同一個位置)
? ? ? ? ? ? ?執行三: 線程A解掛,接著執行(看到的仍是舊表),即從transfer代碼(1)處接著執行,當前的 e = 3, next = 7, 上面已經描述。
? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1. 處理元素 3 , 將 3 放入 線程A自己棧的新table中(新table是處于線程A自己棧中,是線程私有的,不肥線程2的影響),處理3后的圖如下:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2. ?線程A再復制元素 7 ,當前 e = 7 ,而next值由于線程 B 修改了它的引用,所以next 為 3 ,處理后的新表如下圖
?
? ? ? ? ? ? ? ? ? ? ? ? ? ?3. 由于上面取到的next = 3, 接著while循環,即當前處理的結點為3, next就為null ,退出while循環,執行完while循環后,新表中的內容如下圖:
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 4. 當操作完成,執行查找時,會陷入死循環!
?
?
下文轉自https://www.cnblogs.com/yjbjingcha/p/6957909.html
HashMap在高并發下引起的死循環
HashMap事實上并非線程安全的,在高并發的情況下,是非常可能發生死循環的,由此造成CPU 100%,這是非常可怕的。所以在多線程的情況下,用HashMap是非常不妥當的行為,應採用線程安全類ConcurrentHashMap進行取代。
?
?
HashMap死循環原因
?
HashMap進行存儲時,假設size超過當前最大容量*負載因子時候會發生resize。首先看一下resize原代碼
?
?
void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}Entry[] newTable = new Entry[newCapacity];transfer(newTable);table = newTable;threshold = (int)(newCapacity * loadFactor);}而這段代碼中又調用了transfer()方法,而這種方法實現的機制就是將每一個鏈表轉化到新鏈表,而且鏈表中的位置發生反轉,而這在多線程情況下是非常easy造成鏈表回路。從而發生get()死循環。我們看一下他的源碼
?
?
void transfer(Entry[] newTable) {Entry[] src = table;int newCapacity = newTable.length;for (int j = 0; j < src.length; j++) {Entry<K,V> e = src[j];if (e != null) {src[j] = null;do {Entry<K,V> next = e.next;int i = indexFor(e.hash, newCapacity);e.next = newTable[i];newTable[i] = e;e = next;} while (e != null);}}}?
?
HashMap死循環演示
假如有兩個線程P1、P2,以及鏈表 a=》b=》null?
1、P1先運行,運行完"Entry<K,V> next = e.next;"代碼后發生堵塞,或者其它情況不再運行下去,此時e=a。next=b
2、而P2已經運行完整段代碼,于是當前的新鏈表newTable[i]為b=》a=》null
3、P1又繼續運行"Entry<K,V> next = e.next;"之后的代碼,則運行完"e=next;"后,newTable[i]為a《=》b。則造成回路,while(e!=null)一直死循環
?
?
總結
HashMap并不是線程安全,所以在多線程情況下,應該首先考慮用ConcurrentHashMap。避免悲劇的發生?
?
轉載于:https://www.cnblogs.com/panxuejun/p/8577410.html
總結
以上是生活随笔為你收集整理的并发的HashMap为什么会引起死循环?(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ 高质量程序设计指南读书笔记
- 下一篇: 4、linux网络编程--套接字的介绍