public final class ClientGateway {
private static ClientGateway instance;
private static List<NetworkClientListener> listeners = Collections.synchronizedList(new ArrayList<NetworkClientListener>());
private static final Object listenersMutex = new Object();
protected EventHandler eventHandler;
private ClientGateway() {
eventHandler = new EventHandler();
}
public static synchronized ClientGateway getInstance() {
if (instance == null)
instance = new ClientGateway();
return instance;
}
public void addNetworkListener(NetworkClientListener listener) {
synchronized (listenersMutex) {
listeners.add(listener);
}
}
class EventHandler {
public void onLogin(final boolean isAdviceGiver) {
new Thread() {
public void run() {
synchronized (listenersMutex) {
for (NetworkClientListener nl : listeners)
nl.onLogin(isAdviceGiver);
}
}
}.start();
}
}
}
这段代码会抛出ConcurrentModificationException异常。但是我认为,如果它们都在listenersMutex上同步,那么它们应该按顺序执行,所有操作listeners列表的函数内部的所有代码都在同步块中运行,这些同步块在Mutex上同步。修改列表的唯一代码是addNetworkListener(...)和removeNetworkListener(...),但目前从未调用removeNetworkListener。
错误似乎是在onLogin函数/线程迭代监听器时仍在添加NetworkClientListener。
感谢您的见解!
编辑:NetworkClientListener是一个接口,并且“onLogin”的实现留给实现函数的编码者,但是他们对函数的实现无法访问listeners List。
另外,我刚刚完全重新检查了代码,除了addNetworkListener()和removeNetworkListener()函数之外,没有修改列表的代码,其他函数只迭代列表。将代码从以下方式更改:
for (NetworkClientListener nl : listeners)
nl.onLogin(isAdviceGiver);
致:
for(int i = 0; i < listeners.size(); i++)
nl.onLogin(isAdviceGiver);
似乎解决了并发问题,但我已经知道这个问题的原因,想知道最初是什么导致了这个问题。感谢您一直以来的帮助!
异常: Exception in thread "Thread-5" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:782) at java.util.ArrayList$Itr.next(ArrayList.java:754) at chapchat.client.networkcommunication.ClientGateway$EventHandler$5.run(ClientGateway.java:283)
编辑:好吧,我感觉有点蠢。但是感谢您所有人的帮助!特别感谢MJB和jprete!
答案:某个人在onLogin()的实现中向网关添加了一个新的监听器。因此(since java's synchronization is based on Threads and is reentrant, so that a Thread may not lock on itself),当调用onLogin()时,在他的实现中,我们正在遍历监听器,并在此过程中,添加了一个新的监听器。
解决方案:使用CopyOnWriteArrayList而不是同步列表(MJB的建议)。