Я использую Collection
( HashMap
используется JPA косвенно, так бывает), но, по-видимому, случайным образом код генерирует ConcurrentModificationException
. Что вызывает это и как решить эту проблему? Возможно, используя некоторую синхронизацию?
Вот полная трассировка стека:
Exception in thread "pool-1-thread-1" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$ValueIterator.next(Unknown Source)
at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:555)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
java
exception
collections
concurrentmodification
mainstringargs
источник
источник
Ответы:
Это не проблема синхронизации. Это произойдет, если базовая коллекция, по которой выполняется итерация, будет изменена чем-либо, кроме самого Iterator.
Это вызовет
ConcurrentModificationException
приit.hasNext()
повторном вызове.Правильный подход был бы
Предполагая, что этот итератор поддерживает
remove()
операцию.источник
Попробуйте использовать
ConcurrentHashMap
вместо простогоHashMap
источник
Модификация в
Collection
то время как перебор , что сCollection
использованиемIterator
это не разрешаются большинствомCollection
классов. Библиотека Java называет попытку изменить элементCollection
во время итерации как «одновременное изменение». К сожалению, это говорит о том, что единственная возможная причина - одновременная модификация несколькими потоками, но это не так. Используя только один поток, можно создать итератор дляCollection
(с использованиемCollection.iterator()
или расширенногоfor
цикла ), начать итерацию (использоватьIterator.next()
или, что эквивалентно, ввести тело расширенногоfor
цикла), изменитьCollection
, а затем продолжить итерацию.Чтобы помочь программистам, некоторые реализации этих
Collection
классов пытаются обнаружить ошибочную одновременную модификацию и выдают,ConcurrentModificationException
если они это обнаруживают. Однако, как правило, невозможно и практически невозможно гарантировать обнаружение всех одновременных модификаций. Так что ошибочное использованиеCollection
не всегда приводит к броскуConcurrentModificationException
.В документации
ConcurrentModificationException
говорится:Обратите внимание, что
Документация из
HashSet
,HashMap
,TreeSet
иArrayList
классов говорит , что это:Обратите внимание еще раз, что поведение «не может быть гарантировано» и только «на основе максимальных усилий».
В документации к нескольким методам
Map
интерфейса сказано следующее:Заметим еще раз, что для обнаружения требуется только «основа максимальных усилий», а
ConcurrentModificationException
явно предлагается только для классов, которые не работают одновременно (небезопасные для потоков).Отладка
ConcurrentModificationException
Итак, когда вы видите трассировку стека из-за a
ConcurrentModificationException
, вы не можете сразу предположить, что причиной является небезопасный многопоточный доступ к файлуCollection
. Вы должны изучить трассировку стека, чтобы определить, какой классCollection
вызвал исключение (метод этого класса прямо или косвенно вызовет его) и для какогоCollection
объекта. Затем вы должны изучить, откуда этот объект может быть изменен.Collection
внутри расширенногоfor
цикла надCollection
. То, что вы не видитеIterator
объекта в исходном коде, не означает, что его там нетIterator
! К счастью, один из операторов неисправногоfor
цикла обычно находится в трассировке стека, поэтому отследить ошибку обычно легко.Collection
объект. Обратите внимание, что неизменяемые представления коллекций (например, созданные с помощьюCollections.unmodifiableList()
) сохраняют ссылку на изменяемую коллекцию, поэтому итерация по «неизменяемой» коллекции может вызвать исключение (модификация была сделана в другом месте). Другие взгляды из вашихCollection
, таких как подсписки ,Map
наборы входных иMap
наборы ключей также сохраняют ссылки на оригинал (изменяемый)Collection
. Это может быть проблемой даже для потоковообеспеченныхCollection
, таких какCopyOnWriteList
; не предполагайте, что потокобезопасные (параллельные) коллекции никогда не могут вызвать исключение.Collection
, в некоторых случаях может быть неожиданным. Например,LinkedHashMap.get()
изменяет свою коллекцию .Программирование для предотвращения ошибок одновременной модификации
По возможности ограничивайте все ссылки
Collection
объектом, чтобы было легче предотвратить одновременные изменения. Сделайте объектCollection
aprivate
или локальную переменную и не возвращайте ссылки наCollection
итераторы из методов. Тогда гораздо легче обследовать все места, гдеCollection
можно изменить. ЕслиCollection
должен использоваться несколькими потоками, тогда целесообразно гарантировать, что потоки обращаются к немуCollection
только с соответствующей синхронизацией и блокировкой.источник
В Java 8 вы можете использовать лямбда-выражение:
источник
Это звучит не столько как проблема синхронизации Java, сколько как проблема блокировки базы данных.
Я не знаю, поможет ли добавление версии ко всем вашим постоянным классам, но это один из способов, которым Hibernate может предоставить эксклюзивный доступ к строкам в таблице.
Может быть, уровень изоляции должен быть выше. Если вы разрешаете «грязное чтение», возможно, вам нужно увеличить до сериализуемого.
источник
Попробуйте либо CopyOnWriteArrayList, либо CopyOnWriteArraySet в зависимости от того, что вы пытаетесь сделать.
источник
Я просто привожу здесь свой рабочий пример для новичков, чтобы сэкономить время:
источник
Я столкнулся с этим исключением при попытке удалить x последних элементов из списка.
myList.subList(lastIndex, myList.size()).clear();
было единственным решением, которое сработало для меня.источник