Why does a call to removeListener()
in the following code throw a ConcurrentModificationException
when another thread is using the iterator in fireEvent()
?
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
}
}
}
}
From my understanding, since I'm using synchronized (synchronizedListeners)
in fireEvent()
, this should block any other thread that calls removeListener()
, till the iteration in fireEvent()
is complete at which point it should be safe to remove an element from this Set. But this doesn't seem to be the case. What am I doing wrong?
Possibly related: Java synchronized block vs. Collections.synchronizedMap
Edit: It was pointed out that I was unnecessarily synchronizing the removeListener() method. So I tried this version:
public void removeListener(Object listener) {
synchronizedListeners.remove(listener);
}
But still got the same error.
Edit 2: As assylias pointed out, the problem isn't visible in the above code. I was calling removeListener()
from inside the for loop in the synchronized (synchronizedListeners)
block which was causing the error. The fix I ended up using in this case is to remove the listener from another thread:
public void removeListener(final Object listener) {
new Thread() {
@Override
public void run() {
synchronizedListeners.remove(listener);
}
}.start();
}