2013-07-24 43 views
0

我們正在執行銀行應用程序的負載測試/基準測試。當與大約100個虛擬用戶一起運行時,我們得到ConcurrentModificationException作爲錯誤之一。下面是堆棧跟蹤:從LinkedHashSet創建ArrayList時防止發生ConcurrentModificationException

java.util.ConcurrentModificationException 
    at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:373) 
    at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:384) 
    at java.util.AbstractCollection.toArray(AbstractCollection.java:124) 
    at java.util.ArrayList.<init>(ArrayList.java:131) 
    at my.package.AuthorizationHelper.getAuthModuleList 

下面在getAuthModuleList()這是造成異常的部分:

private static final LinkedHashSet MODULE_SET = new LinkedHashSet(); 

public static List getAuthModuleList(..) 
{ 
    MODULE_SET.clear(); 
    .... 

    MODULE_SET.add(getAllrequiredModules()); 

    List userLevelModules = getAllUserLevelModules(); 

    if (userLevelModules != null) { 
     MODULE_SET.addAll(userLevelModules); 
    } 

    userLevelModules = new ArrayList(MODULE_SET); //Exception here 
    return userLevelModules; 
} 

模塊需要,以便與所需的那些第一因此使用LinkedHashSet執行。

以下是我的事業,爲CME的理解:

  1. 交易用戶A調用此方法。
  2. 與此同時,用戶B也稱這種方法。
  3. 當用戶A到達異常行時,用戶B的併發訪問已經發生變異MODULE_SET
  4. 由於上述原因,ArrayList的實現拋出異常。

如何代碼應該進行修改,以防止上述不破壞功能按我的理解:

改變這一行:

userLevelModules = new ArrayList(MODULE_SET); 

爲了這個片斷:

LinkedHashSet moduleSetCopy = new LinkedHashSet(MODULE_SET); 
// userLevelModules = new ArrayList(MODULE_SET); 
userLevelModules = new ArrayList(moduleSetCopy); 

所以我的問題是,

  • 我的分析是否正確?
  • 我應該使同步副本使用其中一個集合方法或是正常的副本足夠?

注意,應用程序使用甲骨文JRockit的(R)(建R28.2.5-20-152429-1.6.0_37-20120927-1915窗口-x86_64的,編譯模式),而不是標準的Sun JDK。我們需要模擬生產水平的環境,因此不使用後者。

UPDATE:不確定是否與回答相關,但MODULE_SET在方法開始時被清除。

+0

您可以從任何MODULE_SET的修改過的獲得'ConcurrentModificationException'。基本上你需要同步整個方法。, – kiheru

回答

0

哎喲,但願這不是我的銀行如要使用這個應用程序;-)

無論如何,你正在修改沒有適當的同步或鎖定的共享資源(MODULE_SET)和尤爾分析是正確的。但是,如果模塊列表是依賴於用戶或請求的,則該解決方案根本不使用共享資源,或者如果它是真正的單例,則將其初始化一次。

對於第一種選擇,代碼看起來是這樣的:

public static List getAuthModuleList(..) 
{ 
    LinkedHashSet MODULE_SET = new LinkedHashSet(); 

    MODULE_SET.add(getAllrequiredModules()); 
    List userLevelModules = getAllUserLevelModules(); 

    if (userLevelModules != null) { 
     MODULE_SET.addAll(userLevelModules); 
    } 
    userLevelModules = new ArrayList(MODULE_SET); //Exception here 
    return userLevelModules; 
} 
+0

'MODULE_SET'在方法開始時被清除。所以,從技術上講,它實際上並未被用作共享資源。我認爲這是一個例外,因爲它是一個「靜態final」引用?道歉,如果這是相關的,因爲我沒有在問題中提到它... – Vrushank

+0

@Vrushank清除是不夠的。考慮兩個線程同時執行'getAuthModuleList()'的情況。他們仍然使用相同的MODULE_SET。 – kiheru

+0

是的,發生異常是因爲MODULE_SET是「靜態」(只有一個實例,因此共享),並且多個線程同時修改其內容。如果你使MODULE_SET成爲一個局部變量,異常將消失(至少那個)。 –

相關問題