了解并解决ConcurrentModificationException

时间:2017-06-18 16:20:44

标签: java concurrentmodification

我正在处理间歇性的,很难在一大块遗留代码中重现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循环?据我所知,两个片段都在mItems.keySet()中创建了copy的浅表副本。

1 个答案:

答案 0 :(得分:2)

  

我对CME为什么被抛出正确的理解?

绝对。这正是发生的事情。

  

如果用以下内容替换for-each循环,我会避免使用CME吗?

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

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

就修复此问题而言,Java标准库中的WeakHashMap类没有现成的并发替换。您可以通过使addItemremoveItem同步,并在构建copy的循环周围添加同步块,以明显的方式解决此问题。您还可以在不使用代码中的synchronized的情况下查看解决此问题的third-party collections