J'utilise a Collection
(un HashMap
utilisé indirectement par le JPA, c'est le cas), mais apparemment de manière aléatoire, le code lance un ConcurrentModificationException
. Quelle en est la cause et comment résoudre ce problème? En utilisant une certaine synchronisation, peut-être?
Voici le stack-trace complet:
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
la source
la source
Réponses:
Ce n'est pas un problème de synchronisation. Cela se produira si la collection sous-jacente en cours d'itération est modifiée par autre chose que l'itérateur lui-même.
Cela lancera un
ConcurrentModificationException
lorsque leit.hasNext()
sera appelé la deuxième fois.La bonne approche serait
En supposant que cet itérateur prend en charge l'
remove()
opération.la source
Essayez d'utiliser un
ConcurrentHashMap
plutôt qu'un simpleHashMap
la source
La modification d'une
Collection
itération while à l'Collection
aide de anIterator
n'est pas autorisée par la plupart desCollection
classes. La bibliothèque Java appelle une tentative de modification d'unCollection
tout en l'itérant une "modification simultanée". Cela suggère malheureusement que la seule cause possible est la modification simultanée par plusieurs threads, mais ce n'est pas le cas. En utilisant un seul thread, il est possible de créer un itérateur pourCollection
(en utilisantCollection.iterator()
, ou une boucle amélioréefor
), de démarrer l'itération (en utilisantIterator.next()
ou en entrant de manière équivalente le corps de lafor
boucle améliorée ), de modifier leCollection
, puis de continuer l'itération.Pour aider les programmeurs, certaines implémentations de ces
Collection
classes tentent de détecter une modification concurrente erronée et lancent unConcurrentModificationException
si elles la détectent. Cependant, il n'est en général pas possible et pratique de garantir la détection de toutes les modifications concurrentes. Ainsi, une utilisation erronée de l 'Collection
n'entraîne pas toujours un jetConcurrentModificationException
.La documentation de
ConcurrentModificationException
dit:Notez que
La documentation du
HashSet
,HashMap
,TreeSet
et lesArrayList
classes dit ceci:Notez à nouveau que le comportement «ne peut être garanti» et n'est que «au mieux».
La documentation de plusieurs méthodes de l'
Map
interface dit ceci:Notez à nouveau que seule une "base de meilleur effort" est requise pour la détection, et a
ConcurrentModificationException
n'est explicitement suggéré que pour les classes non concurrentes (non thread-safe).Débogage
ConcurrentModificationException
Ainsi, lorsque vous voyez une trace de pile due à a
ConcurrentModificationException
, vous ne pouvez pas immédiatement supposer que la cause est un accès multithread non sécurisé à un fichierCollection
. Vous devez examiner la trace de pile pour déterminer quelle classe de aCollection
lancé l'exception (une méthode de la classe l'aura directement ou indirectement lancée), et pour quelCollection
objet. Ensuite, vous devez examiner d'où cet objet peut être modifié.Collection
intérieur d'unefor
boucle améliorée sur leCollection
. Ce n'est pas parce que vous ne voyez pas d'Iterator
objet dans votre code source qu'il n'y en a pasIterator
! Heureusement, l'une des instructions de lafor
boucle défectueuse se trouve généralement dans la trace de la pile, il est donc généralement facile de localiser l'erreur.Collection
objet. Notez que les vues non modifiables des collections (telles que produites parCollections.unmodifiableList()
) conservent une référence à la collection modifiable, donc l' itération sur une collection "non modifiable" peut lever l'exception (la modification a été faite ailleurs). D'autres vues de votreCollection
, telles que les sous-listes ,Map
les jeux d'entrées etMap
les jeux de clés conservent également des références à l'original (modifiable)Collection
. Cela peut être un problème même pour un thread-safeCollection
, tel queCopyOnWriteList
; ne supposez pas que les collections thread-safe (simultanées) ne peuvent jamais lever l'exception.Collection
peuvent être inattendues dans certains cas. Par exemple,LinkedHashMap.get()
modifie sa collection .Programmation pour éviter les erreurs de modification simultanées
Lorsque cela est possible, limitez toutes les références à un
Collection
objet, il est donc plus facile d'empêcher les modifications simultanées. CréezCollection
unprivate
objet ou une variable locale et ne renvoyez pas de références àCollection
ou à ses itérateurs à partir des méthodes. Il est alors beaucoup plus facile d'examiner tous les endroits où leCollection
peut être modifié. Si leCollection
doit être utilisé par plusieurs threads, il est alors pratique de s'assurer que les threads accèdent auCollection
seul avec une synchronisation et un verrouillage appropriés.la source
Dans Java 8, vous pouvez utiliser l'expression lambda:
la source
Cela ressemble moins à un problème de synchronisation Java qu'à un problème de verrouillage de base de données.
Je ne sais pas si l'ajout d'une version à toutes vos classes persistantes résoudra le problème, mais c'est une façon pour Hibernate de fournir un accès exclusif aux lignes d'une table.
Peut-être que le niveau d'isolement doit être plus élevé. Si vous autorisez les "lectures sales", vous devrez peut-être passer à sérialisable.
la source
Essayez CopyOnWriteArrayList ou CopyOnWriteArraySet en fonction de ce que vous essayez de faire.
la source
Je donne juste mon exemple de travail ici pour les débutants pour gagner du temps:
la source
J'ai rencontré cette exception en essayant de supprimer x derniers éléments de la liste.
myList.subList(lastIndex, myList.size()).clear();
était la seule solution qui fonctionnait pour moi.la source