параллельная модификация массива

Я создаю многопоточный чат в java. Когда пользователь u1 отправляет сообщение пользователю u2, но пользователь u2 не подключен, пользователь u1 отправляет сообщение на сервер, а пользователь u2 получит сообщение, как только он подключится к серверу. Неотправленные сообщения добавляются в список ArrayList. Как только пользователь подключается, он проверяет, является ли он получателем ожидающего сообщения. Если это так, сообщение отправляется ему, а затем удаляется из списка ожидающих сообщений. Вот как я это делаю:

for(Iterator<String> itpendingmsgs = pendingmsgs.iterator(); itpendingmsgs.hasNext();) {
    String pendingmsg = itpendingmsgs.next();
    String dest = pendingmsg.substring(4);              
    if (protocol.author.equals(dest)) {
        sendMsg(msg);
        pendingmsgs.remove(pendingmsg);
    }
}

вот что я получаю:

Exception in thread "Thread-3" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
at ChatServer$ClientConnection.run(ChatServer.java:383)
at java.lang.Thread.run(Unknown Source)

Как это исправить? Это потому, что я использую итератор?


person tony danza    schedule 21.05.2012    source источник
comment
Вы не можете использовать удаление во время итерации   -  person keyser    schedule 21.05.2012


Ответы (4)


Вместо этого

pendingmsgs.remove(pendingmsg);

использовать

itpendingmsgs.remove();

Iterator из ArrayList быстро завершается неудачей , поэтому пока вы повторяете над ArrayList с использованием Iterator, если базовый ArrayList изменен каким-либо методом, кроме add и remove, предоставленным самим Iterator, он выбросит ConcurrentModificationException и выйдет из строя.

В вашей текущей реализации, когда вы перебираете список при определенных условиях, вы также изменяете список, вызывая remove для базового ArrayList, вместо этого вызывая метод remove для Iterator.

Из документов Java:

Итераторы, возвращаемые методами iterator и listIterator этого класса, являются отказоустойчивыми: если список структурно изменен в любой момент после создания итератора любым способом, кроме как с помощью собственных методов удаления или добавления итератора, итератор выдаст исключение ConcurrentModificationException. Таким образом, перед лицом одновременной модификации итератор быстро и чисто дает сбой, а не рискует произвольным, недетерминированным поведением в неопределенное время в будущем.

Обратите внимание, что безотказное поведение итератора не может быть гарантировано, так как, вообще говоря, невозможно дать какие-либо жесткие гарантии при наличии несинхронизированной параллельной модификации. Отказоустойчивые итераторы генерируют исключение ConcurrentModificationException в максимально возможной степени. Поэтому было бы неправильно писать программу, корректность которой зависела бы от этого исключения: безотказное поведение итераторов следует использовать только для обнаружения ошибок.

person mprabhat    schedule 21.05.2012

Вам не разрешено изменять список во время его повторения, за исключением самого экземпляра iterator. Вы должны позвонить itpendingmsgs.remove().

person Marko Topolnik    schedule 21.05.2012

Вместо

pendingmsgs.remove(pendingmsg);

использовать

itpendingmsgs.remove();

Видеть:

Поведение итератора не указано, если базовая коллекция изменяется во время выполнения итерации любым способом, кроме вызова этого метода.

Источник: Java API

person yankee    schedule 21.05.2012

На основе документации ArrayList API < em> Итераторы, возвращаемые методами итератора и listIterator этого класса, являются отказоустойчивыми: если список структурно изменен в любое время после создания итератора любым способом, кроме как с помощью собственных методов удаления или добавления итератора, итератор выдаст исключение ConcurrentModificationException. .

Вы не должны удалять из коллекции во время ее итерации. Вместо этого вы должны использовать метод удаления итератора.

for(Iterator<String> itpendingmsgs = pendingmsgs.iterator(); itpendingmsgs.hasNext();) {
String pendingmsg = itpendingmsgs.next();
String dest = pendingmsg.substring(4);              
if (protocol.author.equals(dest)) {
    sendMsg(msg);
    itpendingmsgs.remove();
}

}

person HPCS    schedule 21.05.2012