0

我们正在执行银行应用程序的负载测试/基准测试。当使用大约 100 个虚拟用户运行时,我们遇到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 的并发访问已经 mutated MODULE_SET
  4. 由于上述原因,ArrayList 的实现会抛出异常。

根据我的理解,应如何修改代码以防止出现上述情况而不会破坏功能:

更改此行:

userLevelModules = new ArrayList(MODULE_SET);

到这个片段:

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

所以我的问题是,

  • 我的分析正确吗?
  • 我应该使用 Collections 方法之一制作同步副本还是普通副本就足够了?

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

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

4

1 回答 1

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;
 }
于 2013-07-24T10:03:58.100 回答