了解并解决 ConcurrentModificationException

Understanding and resolving ConcurrentModificationException

我正在处理一段遗留代码中间歇性的、难以重现的问题ConcurrentModificationException

class LegacyCode {
    private final WeakHashMap<A, B> mItems = new WeakHashMap<A, B>();

    public void someWork() {
        final List<A> copy = new LinkedList<A>();

        for (final A item : mItems.keySet()) {
            copy.add(item);
        }

        for (final A item : copy) {
            item.someMethod();
        }
    }

    public void addItem(final A item) {
        mItems.put(item, new B());
    }

    public void removeItem(final A item) {
        mItems.remove(item);
    }
}

正在投入 CME:

for (final A item : mItems.keySet()) {
    copy.add(item);
}

我不完全确定我们为什么要以这种方式创建 copy。抛出 CME 是因为在 for-each 循环为 运行 时调用了 addItem(A)removeItem(A)

问题

  1. 我对为什么抛出 CME 的理解正确吗?

  2. 如果我将 for-each 循环替换为:

    ,我会避免 CME 吗?

    final List<A> copy = new LinkedList<A>(mItems.keySet());

  3. 此更改是否等同于我们将要替换的 for-each 循环?据我所知,这两个片段都在 copy.

  4. 中创建了 mItems.keySet() 的浅表副本

Is my understanding of why CME is being thrown correct?

当然可以。这正是正在发生的事情。

Will I avoid CME if I replace the for-each loop with:

final List<A> copy = new LinkedList<A>(mItems.keySet());

不,你不会,因为 LinkedList<A> 的构造函数会有类似的循环。因此,您说此更改将等同于我们将替换的 for-each 循环是正确的。

就解决这个问题而言,Java 标准库中没有现成的 WeakHashMap class 的并发替代品。您可以通过使 addItemremoveItem 同步并在构造 copy 的循环周围添加同步块来解决此问题。您还可以查看解决此问题的 ,而无需在代码中使用 synchronized