HashMap在併發環境下的死循環分析[copy]

今天在看《分佈式java應用》這本書的時候看到作者提到HashMap在多線程併發的環境下有可能出現死循環,導致cpu100%的現象,看了下源碼結合網上的分析說明下這種可能性。可能出現問題的地方是在擴容的時候

  1. void resize(int newCapacity) {  
  2.         Entry[] oldTable = table;  
  3.         int oldCapacity = oldTable.length;  
  4.         if (oldCapacity == MAXIMUM_CAPACITY) {  
  5.             threshold = Integer.MAX_VALUE;  
  6.             return;  
  7.         }  
  8.   
  9.         Entry[] newTable = new Entry[newCapacity];  
  10.         transfer(newTable);  
  11.         table = newTable;  
  12.         threshold = (int)(newCapacity * loadFactor);  
  13.     }  

 

這個方法本身沒有問題,問題出在transfer(newTable);這個方法是用來移動oldTable裏的數據到newTable裏。

  1. /** 
  2.     * Transfers all entries from current table to newTable. 
  3.     */  
  4.    void transfer(Entry[] newTable) {  
  5.        Entry[] src = table;  
  6.        int newCapacity = newTable.length;  
  7.        for (int j = 0; j < src.length; j++) {  
  8.         //(1)  
  9.            Entry<K,V> e = src[j];  
  10.            if (e != null) {  
  11.                src[j] = null;  
  12.                do {  
  13.                 //(2)  
  14.                    Entry<K,V> next = e.next;  
  15.                    int i = indexFor(e.hash, newCapacity);  
  16.                 //(3)  
  17.                    e.next = newTable[i];  
  18.                    newTable[i] = e;  
  19.                    e = next;  
  20.                } while (e != null);  
  21.            }  
  22.        }  
  23.    }  

 

下面分析可能出現的情況,假設原來table裏存放的entry鏈表順序是:

oldTable[i]:a1,a2,null

線程P1運行到(1)下面這行時,e=a1(a1.next=a2),執行到(2)下面時:next=e.next=a2。這個時候切換到線程P2,線程P2執行完這個鏈表的循環,如果剛好a1和a2在newTable裏的i值相同(int i = indexFor(e.hash, newCapacity);),那麼此時的鏈表順序是:

newTable[i]:a2(next=a1),a1,null

現在cpu重新切回P1,在(3)這行:e.next = newTable[i];即:a1.next=newTable[i]=a2;

然後:newTable[i]=e=a1;e = next=a2;可以看到這個時候a1.next=a2,a2.next=a1,形成迴環了,這樣就造成了死循環.下面是針對各個線程各變量的情況

init(初始值):a1.next=a2,a2.next=null
 P1:e=a1,next=e.next=a2; waiting
 P2:a2.next=a1,a1.next=null ;notify
 P1:e.next=a1.next=newTable[i]=a2; newTable[i]=a1,e=next=a2
 end:a1.next=a2;a2.next=a1(P2結束後產生的結果)

可以看到很偶然的情況下會出現死循環,不過一旦出現後果是非常嚴重的,多線程的環境還是應該用ConcurrentHashMap。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章