java中HashMap在多線程環境下引起CPU100%的問題解決

來源:互聯網
上載者:User

標籤:

 

       最近項目中出現了Tomcat佔用CPU100%的情況,原以為是代碼中出現死迴圈,後台使用jstack做了dump,發現是系統中不合理使用HashMap導致出現了死迴圈(注意不是死結)。

 

       產生這個死迴圈的根源在於對一個未保護的共用變數 — 一個"HashMap"資料結構的操作。當在所有操作的方法上加了"synchronized"後,一切恢複了正常。

       這算jvm的bug嗎?應該說不是的,這個現象很早以前就報告出來了(詳細見:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457)。Sun的工程師並不認為這是bug,而是建議在這樣的情境下應採用"ConcurrentHashMap",

回複中的原話:

This is a classic symptom of an incorrectly synchronized use ofHashMap. Clearly, the submitters need to use a thread-safeHashMap. If they upgraded to Java 5, they could just use ConcurrentHashMap. 

 

所以在開發過程中應當注意這點,在多線程的環境下,盡量使用ConcurrentHashMap。

可能出現問題的地方是在擴容的時候

Java代碼  
  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裡。

Java代碼  
  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.    }    

 下面分析可能出現的情況,假設原來oldTable裡存放a1,a2的hash值是一樣的,那麼entry鏈表順序是:

P1:oldTable[i]->a1->a2->null

P2:oldTable[i]->a1->a2->null

線程P1運行到(1)下面這行時,e=a1(a1.next=a2),繼續運行到(2)下面時,next=a2。這個時候切換到線程P2,線程P2執行完這個鏈表的迴圈。如果恰a1,a2在新的table中的hash值又是一樣的,那麼此時的鏈表順序是: 

主存:newTable[i]->a2->a1->null

注意這個時候,a1,a2串連順序已經反了。現在cpu重新切回P1,在(3)這行以後:
e.next = newTable[i];
即:a1.next=newTable[i];

newTable[i]=a1;

e=a2;

開始第二次while迴圈(e=a2,next=a1):

a2.next=newTable[i];//也就是a2.next=a1

newTable[i]=a2

e=a1

開始第三次while迴圈(e=a1,next=null)

a1.next=newTable[i];//也就是a1.next=a2

 

這個時候a1.next=a2,a2.next=a1,形成迴環了,這樣就造成了死迴圈,在get操作的時候next永遠不為null,造成死迴圈。

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

http://shuaijie506.iteye.com/blog/1815213

 http://blog.163.com/huxb23%40126/blog/static/625898182011211318854/

java中HashMap在多線程環境下引起CPU100%的問題解決(轉)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.