Есть класс:
@Component
public class EventPublisherImpl implements EventPublisher {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private List<EventListener> listeners = new ArrayList<>();
@Override
public void addListener(EventListener toAdd) {
listeners.add(toAdd);
}
@Override
public void removeListener(EventListener toRemove) {
listeners.remove(toRemove);
}
public void publicEvent(AmiObject amiObject) {
if (listeners != null && !listeners.isEmpty()) {
Iterator<EventListener> iterator = listeners.iterator();
while (iterator.hasNext())
synchronized (iterator) {
EventListener eventListener = iterator.next();
if (eventListener != null) {
eventListener.publicEvent(amiObject);
}
}
}
}
}
EventListener'ы добавляются в него из разных нитей.
Так вот при запуске возникает ошибка:
Exception in thread "Thread-1" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
at ...EventPublisherImpl.publicEvent(EventPublisherImpl.java:34)
at ...AmiObjectParserImpl.parseStr(AmiObjectParserImpl.java:45)
at ...ConnectorImpl.listenSocket(ConnectorImpl.java:214)
at ...ConnectorImpl.run(ConnectorImpl.java:92)
at java.base/java.lang.Thread.run(Thread.java:834)
и указывает на строчку
EventListener eventListener = iterator.next();
Итератор ArrayList
не поддерживает параллельное изменение списка кроме изменений через методы самого итератора. При обнаружении параллельного изменения выбрасывается ConcurrentModificationException
об этом написано в документации:
The iterators returned by this class's iterator
and listIterator
methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException
.
К тому же сам ArrayList
не является потокобезопасным, это может привести к другим ошибкам и расхождениям в данных. То, что список несинхронизирован и что нужно делать для безопасной работы из нескольких потоков тоже написано в документации:
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists, the list should be "wrapped" using the Collections.synchronizedList
method. This is best done at creation time, to prevent accidental unsynchronized access to the list:
List list = Collections.synchronizedList(new ArrayList(...));
Чтобы исправить код нужно поступить согласно рекомендациям.
Collections.synchronizedList
:private List<EventListener> listeners = Collections.synchronizedList(new ArrayList<EventListener>());
synchronized(listeners) {
for(EventListener l : listeners) {
l.publicEvent(amiObject);
}
}
Захват такого монитора
synchronized (iterator) {...}
не имеет смысла, т.к. listeners.iterator();
возвращает каждому зашедшему потоку новый объект - итератор, пруф из класса ArrayList
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
синхронизируйтесь на пример на самой коллекции
synchronized (listeners) {...}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
В своем андроид приложении я использую NDK и у меня есть вот такой метод
Сделал pull проекта на другой компьютерТеперь Idea подчеркивает названия View в контроллере, которые я разрешаю TilesViewResolver, в jsp не видит модель...
Пишу код в IIDEAЯ что-то такое нажал и метод Color теперь перечёркнутый