【问题标题】:ArrayList iterator throwing ConcurrentModificationExceptionArrayList 迭代器抛出 ConcurrentModificationException
【发布时间】:2017-10-18 13:35:39
【问题描述】:

我有一个带有两个访问器方法和一个通知器的 ArrayList。我的清单:

private final List<WeakReference<LockListener>> listeners = new ArrayList<>();

所有订阅操作都使用这个:

public void subscribe(@NonNull LockListener listener) {
    for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext(); ) {
        // has this one already subscribed?
        if (listener.equals(it.next().get())) {
            return;
        }
    }
    listeners.add(new WeakReference<>(listener));
}

所有退订操作都使用这个:

public void unsubscribe(@NonNull LockListener listener) {
    if (listeners.isEmpty()) {
        return;
    }

    for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext(); ) {
        WeakReference<LockListener> ref = it.next();
        if (ref == null || ref.get() == null || listener.equals(ref.get())) {
            it.remove();
        }
    }
}

还有通知者:

private void notifyListeners() {
    if (listeners.isEmpty()) {
        return;
    }

    Iterator<WeakReference<LockListener>> it = listeners.iterator();
    while (it.hasNext()) {
        WeakReference<LockListener> ref = it.next();
        if (ref == null || ref.get() == null) {
            it.remove();
        } else {
            ref.get().onLocked();
        }
    }
}

我在测试中看到的是 notifyListeners() 中的 it.next() 偶尔会引发 ConcurrentModificationException。我的猜测是,这是由于订阅者方法中的 listeners.add() 造成的。

我想我在这里对迭代器有误解。我假设迭代列表可以保护我免受添加/删除操作引起的并发问题。

显然我在这里错了。在更改您正在迭代的集合时,迭代器是否只是对 ConcurrentModificationException 的保护?例如,在迭代时对列表调用 remove() 会引发错误,但调用 it.remove() 是安全的。

在我的例子中,订阅调用 add() 在同一个列表上,因为它正在被迭代。我的理解正确吗?

【问题讨论】:

  • 如果你阅读了迭代器的文档,它会告诉你不能修改底层结构

标签: java loops arraylist concurrency iterator


【解决方案1】:

如果我正确阅读了您的最后一句话,则您示例中的三个方法是从多个线程同时调用的。如果确实如此,那么这就是你的问题。

ArrayList 不是线程安全的。在没有额外同步的情况下同时修改它会导致未定义的行为,无论是直接修改还是使用迭代器。

您可以同步对列表的访问(例如,使三个方法同步),或者使用线程安全的集合类,如 ConcurrentLinkedDeque。如果是后者,请务必阅读 JavaDoc(尤其是关于迭代器每周一致的部分)以了解什么是保证的,什么不是。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-19
    • 2020-09-25
    • 2017-11-11
    • 1970-01-01
    • 2022-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多