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, то они должны выполняться последовательно? Весь код в функциях, которые работают со списком слушателей, работает в синхронизированных блоках, которые синхронизируются на мьютексе. Единственным кодом, который изменяет список, являются addNetworkListener(...) и removeNetworkListener(...), но removeNetworkListener никогда не вызывается в данный момент.
Что, по-видимому, происходит с ошибкой, так это то, что NetworkClientListener все еще добавляется, пока функция/поток onLogin выполняет итерацию слушателей.
Спасибо за ваше понимание!
EDIT: NetworkClientListener является интерфейсом и оставляет реализацию onLogin кодировщику, реализующему функцию, но его реализация функции не имеет доступа к списку слушателей.
Кроме того, я только что полностью перепроверил, и нет никаких изменений списка вне функций addNetworkListener() и removeNetworkListener(), другие функции только перебирают список. Изменение кода с:
for (NetworkClientListener nl : listeners)
nl.onLogin(isAdviceGiver);
To:
for(int i = 0; i < listeners.size(); i++)
nl.onLogin(isAdviceGiver);
По-видимому, решает проблему параллелизма, но я уже знал об этом и хотел бы знать, в первую очередь, что вызывает это.
Еще раз спасибо за вашу постоянную помощь!
Исключение: Исключение в потоке "Thread-5" java.util.ConcurrentModificationException в java.util.ArrayList$Itr.checkForComodification(ArrayList.java:782) в java.util.ArrayList$Itr.next(ArrayList.java:754) в chapchat.client.networkcommunication.ClientGateway$EventHandler$5.run(ClientGateway.java:283)
ИЗМЕНИТЬ Хорошо, я чувствую себя немного глупо. Но спасибо за всю вашу помощь! Особенно MJB & jprete!
Ответ: Чья-то реализация onLogin() добавила к шлюзу нового слушателя. Поэтому (поскольку синхронизация java основана на потоках и является реентерабельной, поэтому поток не может блокироваться сам по себе), когда onLogin() был вызван, мы в его реализации, мы перебирали слушателей и в середине этого, добавляя новый слушатель.
Решение: предложение MJB использовать CopyOnWriteArrayList вместо синхронизированных списков.