java.util.ConcurrentModificationException 异常的解决方法

1 发现问题

在迭代 Set 的过程中,删除了其中的某个元素,这时系统会抛出 ConcurrentModificationException 异常。

代码如下:

Set<String> activeActivityNames = findActiveActivityNames(businessId);
for (CanRollBackActivity activity : data) {
    if (activeActivityNames.contains(activity.getActivityName())) {
        data.remove(activity);
    }
}

2 分析问题

我们先来看看这个 ConcurrentModificationException 类。

当方法检测到对象正在进行并发修改,但实际又不允许这种修改时,就会抛出此异常。

例如,某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该这个 Collection。通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器的实现(包括 JRE 提供的所有通用 collection 实现)可能会选择抛出此异常。执行抛出异常操作的迭代器称为快速失败 迭代器,因为迭代器很快就会失败,而不会出现在将来的某个时间上发生不确定行为的风险。

注意,迭代器的快速失败行为无法得到保证,因为不可能对是否出现不同步的并发修改做出任何硬性的保证。快速失败操作会尽最大努力抛出 ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。

我们在代码中使用了一个 Set(Collection 的一种),然后在迭代的过程中删除了其中的某个元素,所以自然会抛出 ConcurrentModificationException 异常咯。

3 解决方法

使用 Iterator 的 remove 方法。这个方法会从迭代器指向的 collection 中移除迭代器返回的最后一个元素。

注意:每次调用 next 只能调用一次此方法。如果进行迭代时用调用此方法之外的其他方式修改了该迭代器所指向的 collection,则迭代器的行为是不确定的。

修复后的代码:

Iterator iterator = data.iterator();
while (iterator.hasNext()) {
    CanRollBackActivity activity = iterator.next();
    if (activeActivityNames.contains(activity.getActivityName())) {
        iterator.remove();
    }
}

你可能感兴趣的:(Java)