【发布时间】: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