更新時間:2022-01-05 10:39:22 來源:動力節點 瀏覽862次
當你研究Java并發送一個容器和框架時,當你想使用ConcurrentHashMap時,原因之一是:HashMap中的線程是不安全的,并發執行PUT操作時會導致死亡循環,因為多線程會導致Hashmap的entry 鏈表構成環形數據結構,一看就會陷入死循環。
1.HashMap添加元素時,HashMap容器??的擴展會導致原理不再解釋,直接附上代碼,如下:
/**
*
* Add elements to the table. If the element is inserted, the Table length is not enough, and the resize method will be called.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
/**
* Resize () method is as follows, it is important to add the elements in the old table to a new table.
*/
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);
}
2.參考上面的代碼,介紹了Transfer方法,(介紹)這就是造成死循環的根本原因,結合TRANSFER的源碼,講解死循環的原理,先上TRANSFER代碼(這是JDK7的源碼),如下:
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next; ---------------------(1)
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} // while
}
}
3.假設:
Map<Integer> map = new HashMap<Integer>(2); // Only two elements can be placed, where Threshold is 1 (when only one element is filled in the table), that is, the insertion element is 1 when the element is 1 (known by the Addentry method)
// Place 2 elements 3 and 7, to place the element 8 (not equal to 1 after the Hash mapping), can cause expansion
假設放置結果如下:
現在有兩個線程A和B,都實現了,即向表中添加元素,即線程a和線程b會看到上面的狀態快照。
執行順序如下:
Execute 1:線程A在Transfer function(1)中執行(在Transfer function代碼中標注)。此時棧中的線程a
e = 3
next = 7
執行2:線程B 執行Transfer函數中的While循環,將原表變成新表(線程b自己的棧),然后寫入內存。如下圖(假設新的Hash函數下兩個元素會映射到同一個位置)
執行三:線程A敲了,然后執行(還是老表看到的),即從Transfer代碼(1),當前E=3,Next=7,上面已經說明了。
(1)處理元素3,將3放入線程A棧中的新表中(新表在線程A棧中,是線程私有的影響,未施肥線程2),處理3后的繪圖3如下:
(2)線程A復制了元素7,當前E=7,由于線程b被修改,next值已經修改了它的引用,所以NEXT為3,處理后的新表如下圖。
(3)由于上面的next=3,那么While循環,即當前進程為3,next為NULL,退出while循環,執行While循環后,new表中的內容如下:
(4)操作完成后會陷入HashMap高并發死循環!
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習