在主机上 运行 代码时获取 ConcurrentModificationException

Getting ConcurrentModificationException when running code on hosts

当 运行 在生产环境中执行以下代码时,我收到 ConcurrentModificationException。该问题似乎是间歇性的(当我尝试调用使用以下代码的 api 时,它工作正常)。

我检查过类似的问题,但在每个问题中,有人会遍历列表并同时修改它。我不确定下面的代码有什么问题,为什么会抛出异常。非常感谢任何指点。

谢谢。

private String getLookupKeyForODPair () {
    
    final List<LookUpKey> lookUpKeyList = transitTimeConfiguration.getLookUpKeys();
    
    // Build lookUpKey
    final String lookUpKey = buildLookupKey(lookUpKeyList, originDestinationPair, merchantId);

    // perform operations on lookupKey and not on lookupKeyList
    
}

private String buildLookupKey(final List<LookUpKey> lookUpKeyList,
                              final ODPair odPair,
                              final String id) {
    lookUpKeyList.sort(Comparator.comparing(LookUpKey::getOrder));
    
    final List<String> keyList = new ArrayList<>();
    
    lookUpKeyList.forEach(key -> {
        switch (key.getName()) {
            case Constants.ID:
                keyList.add(id);
                break;
            case Constants.ZIP_CODE:
                keyList.add(odPair.zipCode);
                break;
            case Constants.COUNTRY_CODE:
                keyList.add(odPair.countryCode);
                break;
            default:
    
        }
    });
    
    return String.join(MiscellaneousConstants.COMMA, keyList);
}

堆栈跟踪

Caused by: java.util.ConcurrentModificationException
    at java.util.ArrayList.forEach(ArrayList.java:1262) ~[?:1.8.0_282]
    at com.placing.lookupKeyService.component.impl.LookupKeyComponentImpl.buildLookupKey(LookupKeyComponentImpl.java:410) ~[LookupKeyService-1.0.jar:?]
    at com.placing.lookupKeyService.component.impl.LookupKeyComponentImpl.getLookupKeyForODPair(LookupKeyComponentImpl.java:221) ~[LookupKeyService-1.0.jar:?]
    at com.placing.lookupKeyService.component.impl.LookupKeyComponentImpl.lambda$null(LookupKeyComponentImpl.java:196) ~[LookupKeyService-1.0.jar:?]
    at java.lang.Iterable.forEach(Iterable.java:75) ~[?:1.8.0_282]
    at com.placing.lookupKeyService.component.impl.LookupKeyComponentImpl.lambda$getLookupKeyForAllConfigurations(LookupKeyComponentImpl.java:196) ~[LookupKeyService-1.0.jar:?]
    at java.util.ArrayList.forEach(ArrayList.java:1259) ~[?:1.8.0_282]
    at com.placing.lookupKeyService.component.impl.LookupKeyComponentImpl.getLookupKeyForAllConfigurations(LookupKeyComponentImpl.java:185) ~[LookupKeyService-1.0.jar:?]
    at com.placing.lookupKeyService.component.impl.LookupKeyComponentImpl.lambda$getAllLookupKeys(LookupKeyComponentImpl.java:160) ~[LookupKeyService-1.0.jar:?]

我实际上不确定是不是该代码引发了异常,异常的堆栈跟踪打印将有助于确定原因。

但是,您提到了在使用 ArrayList 时可能遇到的一个经典问题,特别是在多线程环境中。
事实上,很容易遇到 ConcurrentModificationException,首先是因为遍历 java.util.ArrayList(隐式或显式)时创建的迭代器采用“快速失败”行为。这意味着每次您尝试在遍历列表时修改列表并且不使用迭代器本身定义的方法 removeadd 时,都会立即抛出 ConcurrentModificationException

对于您的具体情况,您没有提供任何关于该方法之外发生的事情的信息,但我可以想象 lookUpKeyList 列表在其他线程的其他地方被修改,而您正在 buildLookupKey 方法时使用 forEach
这也可以解释为什么错误会间歇性发生,因为在使用 ArrayList 的多线程环境中工作时,您可能会遇到不可预测的情况。
这是该特定列表的默认行为,您对此无能为力。

但是,还有其他类型的列表可以帮助您,这取决于您的情况。
例如,您可以使用 java.util.concurrent.CopyOnWriteArrayList

我建议你用 CopyOnWriteArrayList 包裹你的列表,这样可以保证永远不会抛出 ConcurrentModificationException,因为每次调用 add 时,remove在列表上调用,会创建列表的新副本,下划线。

举个例子,你应该有这样的东西:

import java.util.concurrent.CopyOnWriteArrayList;
...
private String buildLookupKey(final List<LookUpKey> lookUpKeyList,
                          final ODPair odPair,
                          final String id) {
    CopyOnWriteArrayList<LookUpKey> copyOflookUpKeyList = new CopyOnWriteArrayList<>(lookUpKeyList);
     
    copyOflookUpKeyList.sort(Comparator.comparing(LookUpKey::getOrder));

    final List<String> keyList = new ArrayList<>();

    copyOflookUpKeyList.forEach(key -> {
        switch (key.getName()) {
            case Constants.ID:
                keyList.add(id);
                break;
            case Constants.ZIP_CODE:
                keyList.add(odPair.zipCode);
                break;
            case Constants.COUNTRY_CODE:
                keyList.add(odPair.countryCode);
                break;
            default:

        }
    });

    return String.join(MiscellaneousConstants.COMMA, keyList);
}

希望这能解决您的问题,但请告诉我更多信息,以便我可以进一步帮助您。