hashMap擴容時應該註意這些死循環問題
hashMap死循環
1.原因: jdk1.7時使用頭插入法 ,1.8之後改成瞭尾插入法解決瞭這個問題
HashMap死循環問題圖解
在HashMap的數組真實長度達到閾值後,會調用擴容方法:
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, initHashSeedAsNeeded(newCapacity)); table = newTable; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); }
這裡可以看到如果有兩個線程A和B,那麼在調用transfer方法之前會在各自的線程中創建新的數組,然後進入到transfer方法中將節點轉移,再看transfer方法:
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; } } }
我在上面的程序中標記瞭一個(1),待會會用到,首先假設HashMap中的結構是這樣的:
那麼線程A如果執行到(1)的位置,那麼e為節點5,next為節點6,這個時候線程B開始運行,在自己的擴容數組裡面運行:
e.next = newTable[i]; newTable[i] = e;
這個時候結構圖:
然後e=next;
在進入循環執行:
e.next = newTable[i]; newTable[i] = e;
這個時候結構為:
然後線程B執行完畢。線程A開始從(1)後面繼續執行,這個時候也是先執行
e.next = newTable[i]; newTable[i] = e;
然後e=next;這個時候e是節點6,然後再進入循環,執行上面兩行程序後的結構如下:
但是這個時候由於6的next是有值的,是節點5,所以再執行e=next;的時候,e不為空,還會進入一次循環,在執行將節點插入頭部的操作,所以這個時候的結構圖:
可以,看到已經成為瞭環狀鏈表,當執行get操作的時候就會產生死循環。
到此這篇關於hashMap擴容時應該註意這些死循環問題的文章就介紹到這瞭,更多相關hashMap擴容內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 淺談HashMap在高並發下的問題
- Java京東面試題之為什麼HashMap線程不安全
- Java1.7全網最深入HashMap源碼解析
- 基於hashmap 的擴容和樹形化全面分析
- Java詳細分析講解HashMap