Estou usando um Collection
(um HashMap
usado indiretamente pelo JPA, acontece), mas aparentemente aleatoriamente o código gera um ConcurrentModificationException
. O que está causando isso e como faço para corrigir esse problema? Usando alguma sincronização, talvez?
Aqui está o rastreamento de pilha completo:
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
fonte
fonte
Respostas:
Este não é um problema de sincronização. Isso ocorrerá se a coleção subjacente que está sendo iterada for modificada por algo diferente do próprio Iterator.
Isso lançará a
ConcurrentModificationException
quando oit.hasNext()
for chamado pela segunda vez.A abordagem correta seria
Supondo que este iterador suporta a
remove()
operação.fonte
Tente usar um em
ConcurrentHashMap
vez de um simplesHashMap
fonte
A modificação de um
Collection
tempo que itera através doCollection
uso de um nãoIterator
é permitida pela maioria dasCollection
classes. A biblioteca Java chama uma tentativa de modificar,Collection
enquanto iterando através dela, uma "modificação simultânea". Infelizmente, isso sugere que a única causa possível é a modificação simultânea por vários threads, mas não é assim. Usando apenas um encadeamento, é possível criar um iterador para oCollection
(usandoCollection.iterator()
ou um loop aprimoradofor
), iniciar a iteração (usandoIterator.next()
ou entrar de forma equivalente no corpo dofor
loop aprimorado ), modificar oCollection
e continuar a iteração.Para ajudar os programadores, algumas implementações dessas
Collection
classes tentam detectar modificações simultâneas erradas e lançam umConcurrentModificationException
se detectarem. No entanto, em geral não é possível e prático garantir a detecção de todas as modificações simultâneas. Portanto, o uso incorreto doCollection
nem sempre resulta em um arremessoConcurrentModificationException
.A documentação do
ConcurrentModificationException
diz:Observe que
A documentação do
HashSet
,HashMap
,TreeSet
eArrayList
aulas diz o seguinte:Observe novamente que o comportamento "não pode ser garantido" e é apenas "na base do melhor esforço".
A documentação de vários métodos da
Map
interface diz o seguinte:Observe novamente que apenas uma "base de melhor esforço" é necessária para a detecção e a
ConcurrentModificationException
é explicitamente sugerida apenas para as classes não simultâneas (sem thread-safe).Depuração
ConcurrentModificationException
Portanto, quando você vê um rastreamento de pilha devido a
ConcurrentModificationException
, não é possível assumir imediatamente que a causa é o acesso multithread inseguro a umCollection
. Você deve examinar o rastreamento de pilha para determinar qual classeCollection
lançou a exceção (um método da classe a lançou direta ou indiretamente) e para qualCollection
objeto. Então você deve examinar de onde esse objeto pode ser modificado.Collection
dentro de umfor
loop aprimorado sobre oCollection
. Só porque você não vê umIterator
objeto no seu código-fonte, não significa que nãoIterator
exista! Felizmente, uma das declarações dofor
loop defeituoso geralmente estará no rastreamento da pilha; portanto, rastrear o erro geralmente é fácil.Collection
objeto. Observe que visualizações não modificáveis de coleções (como produzidas porCollections.unmodifiableList()
) mantêm uma referência à coleção modificável, portanto, a iteração sobre uma coleção "não modificável" pode gerar a exceção (a modificação foi feita em outro lugar). Outros pontos de vista do seuCollection
, como sub-listas ,Map
conjuntos de entrada eMap
conjuntos de chaves também reter referências ao original (modificável)Collection
. Isso pode ser um problema mesmo para um thread-safeCollection
, comoCopyOnWriteList
; não assuma que coleções (simultâneas) seguras para threads nunca podem lançar a exceção.Collection
podem ser inesperadas em alguns casos. Por exemplo,LinkedHashMap.get()
modifica sua coleção .Programação para evitar erros de modificação simultâneos
Quando possível, restrinja todas as referências a um
Collection
objeto, para que seja mais fácil evitar modificações simultâneas. CrieCollection
umprivate
objeto ou uma variável local e não retorne referências aosCollection
métodos ou a seus iteradores. É então muito mais fácil examinar todos os lugares onde os itensCollection
podem ser modificados. Se oCollection
deve ser usado por vários encadeamentos, é prático garantir que os encadeamentos acessem oCollection
único com sincronização e bloqueio apropriados.fonte
No Java 8, você pode usar a expressão lambda:
fonte
Parece menos um problema de sincronização Java e mais um problema de bloqueio de banco de dados.
Não sei se a adição de uma versão a todas as suas classes persistentes resolverá o problema, mas é uma maneira de o Hibernate fornecer acesso exclusivo às linhas de uma tabela.
Pode ser que o nível de isolamento precise ser maior. Se você permitir "leituras sujas", talvez seja necessário aumentar para serializável.
fonte
Tente CopyOnWriteArrayList ou CopyOnWriteArraySet, dependendo do que você está tentando fazer.
fonte
Eu apenas dou meu exemplo de trabalho aqui para iniciantes para economizar seu tempo:
fonte
Corri para essa exceção ao tentar remover x últimos itens da lista.
myList.subList(lastIndex, myList.size()).clear();
foi a única solução que funcionou para mim.fonte