当另一个线程正在使用 fireEvent()
中的迭代器时,为什么在以下代码中调用 removeListener()
会抛出 ConcurrentModificationException
?
public class MyClass {
private Set<Object> synchronizedListeners;
public MyClass() {
synchronizedListeners = Collections.synchronizedSet(
new LinkedHashSet<Object>());
}
public void addListener(Object listener) {
synchronizedListeners.add(listener);
}
public synchronized void removeListener(Object listener) {
synchronizedListeners.remove(listener);
}
public void fireEvent() {
synchronized (synchronizedListeners) {
for (Object listener : synchronizedListeners) {
// do something with listener
}
}
}
}
据我了解,由于我在 fireEvent()
中使用了 synchronized (synchronizedListeners)
,这应该会阻止任何其他调用 removeListener()< 的线程
,直到 fireEvent()
中的迭代完成,此时从该 Set 中删除元素应该是安全的。但事实似乎并非如此。我做错了什么?
可能相关:Java synchronized block vs. Collections.synchronizedMap
编辑:有人指出我不必要地同步了 removeListener() 方法。所以我尝试了这个版本:
public void removeListener(Object listener) {
synchronizedListeners.remove(listener);
}
还是报同样的错
编辑 2: 正如 assylias 指出的那样,问题在上面的代码中不可见。我在导致错误的 synchronized (synchronizedListeners)
block 中的 for 循环内部调用 removeListener()
。在这种情况下,我最终使用的修复方法是从另一个线程中删除监听器:
public void removeListener(final Object listener) {
new Thread() {
@Override
public void run() {
synchronizedListeners.remove(listener);
}
}.start();
}
请您参考如下方法:
您正在同步两个不同的对象。
removeListener
方法在 MyClass
实例上同步,而 fireEvent
中的循环在 synchronizedListeners
上同步> 设置。
您需要做的是在集合本身上同步每个使用 synchronizedListeners
的方法。