从LinkedHashSet创建ArrayList时阻止ConcurrentModificationException

时间:2013-07-24 09:25:54

标签: java collections concurrency

我们正在执行银行应用程序的负载测试/基准测试。当与大约100个虚拟用户一起运行时,我们将ConcurrentModificationException作为错误之一。下面是stacktrace:

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的实现引发了异常。
  5. 根据我的理解,如何修改代码以防止上述内容而不破坏功能:

    更改此行:

    userLevelModules = new ArrayList(MODULE_SET);
    

    到此片段:

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

    所以我的问题是,

    • 我的分析是否正确?
    • 我应该使用其中一种收集方法制作同步副本还是正常副本?

    请注意,该应用程序使用 Oracle JRockit(R)(构建R28.2.5-20-152429-1.6.0_37-20120927-1915-windows-x86_64,编译模式)而不是标准Sun JDK。我们需要模拟生产水平环境,因此不使用后者。

    更新:不确定是否与回答相关,但在方法开始时会清除MODULE_SET

1 个答案:

答案 0 :(得分: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;
 }