Ich benutze a Collection
(a HashMap
wird indirekt von der JPA verwendet, es passiert also), aber anscheinend wirft der Code zufällig a ConcurrentModificationException
. Was verursacht es und wie behebe ich dieses Problem? Vielleicht durch Synchronisation?
Hier ist die vollständige Stapelverfolgung:
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
Hauptstränge
quelle
quelle
Antworten:
Dies ist kein Synchronisationsproblem. Dies tritt auf, wenn die zugrunde liegende Sammlung, über die iteriert wird, durch etwas anderes als den Iterator selbst geändert wird.
Dies wird ein werfen,
ConcurrentModificationException
wenn dasit.hasNext()
das zweite Mal aufgerufen wird.Der richtige Ansatz wäre
Angenommen, dieser Iterator unterstützt die
remove()
Operation.quelle
Versuchen Sie es mit einem
ConcurrentHashMap
anstelle einer EbeneHashMap
quelle
Eine Änderung einer
Collection
Weile, die durch dieCollection
Verwendung von a iteriert,Iterator
ist von den meisten Klassen nicht zulässigCollection
. Die Java-Bibliothek nennt einen ÄnderungsversuchCollection
während des Durchlaufens eine "gleichzeitige Änderung". Das deutet leider darauf hin, dass die einzig mögliche Ursache die gleichzeitige Änderung durch mehrere Threads ist, aber das ist nicht so. Mit nur einem Thread ist es möglich, einen Iterator für dieCollection
(usingCollection.iterator()
oder eine erweitertefor
Schleife ) zu erstellen , mit der Iteration zu beginnen (mitIterator.next()
oder gleichwertig in den Körper der erweitertenfor
Schleife einzutreten ), die zu ändernCollection
und dann die Iteration fortzusetzen.Um Programmierern zu helfen, versuchen einige Implementierungen dieser
Collection
Klassen , fehlerhafte gleichzeitige Änderungen zu erkennen, und werfen ein, wenn sie dies erkennen. Es ist jedoch im Allgemeinen nicht möglich und praktisch, die Erkennung aller gleichzeitigen Änderungen zu gewährleisten. Eine fehlerhafte Verwendung des führt also nicht immer zu einem WurfConcurrentModificationException
Collection
ConcurrentModificationException
.Die Dokumentation von
ConcurrentModificationException
sagt:Beachten Sie, dass
Die Dokumentation der
HashSet
,HashMap
,TreeSet
undArrayList
Klassen , sagt dazu:Beachten Sie erneut, dass das Verhalten "nicht garantiert werden kann" und nur "auf Best-Effort-Basis" ist.
Die Dokumentation mehrerer Methoden der
Map
Schnittstelle besagt Folgendes:Beachten Sie erneut, dass für die Erkennung nur eine "Best-Effort-Basis" erforderlich ist und a
ConcurrentModificationException
explizit nur für nicht gleichzeitige (nicht threadsichere) Klassen empfohlen wird.Debuggen
ConcurrentModificationException
Wenn Sie also aufgrund von a einen Stack-Trace sehen
ConcurrentModificationException
, können Sie nicht sofort davon ausgehen, dass die Ursache ein unsicherer Multithread-Zugriff auf a istCollection
. Sie müssen den Stack-Trace untersuchen, um festzustellen, welche KlasseCollection
die Ausnahme ausgelöst hat (eine Methode der Klasse hat sie direkt oder indirekt ausgelöst) und für welchesCollection
Objekt. Dann müssen Sie prüfen, von wo aus das Objekt geändert werden kann.Collection
innerhalb einer erweitertenfor
Schleife über dieCollection
. Nur weil SieIterator
in Ihrem Quellcode kein Objekt sehen, heißt das nicht, dass es dort keinIterator
gibt! Glücklicherweise befindet sich eine der Anweisungen der fehlerhaftenfor
Schleife normalerweise in der Stapelverfolgung, sodass das Aufspüren des Fehlers normalerweise einfach ist.Collection
Objekt weitergibt . Beachten Sie, dass nicht modifizierbare Ansichten von Sammlungen (wie z. B. vonCollections.unmodifiableList()
) einen Verweis auf die modifizierbare Sammlung beibehalten, sodass eine Iteration über eine "nicht modifizierbare" Sammlung die Ausnahme auslösen kann (die Änderung wurde an anderer Stelle vorgenommen). Andere Ansichten von IhnenCollection
, wie Unterlisten ,Map
Eintragssätze undMap
Schlüsselsätze, behalten ebenfalls Verweise auf das Original bei (änderbar)Collection
. Dies kann selbst für einen Thread-Safe ein Problem seinCollection
, wie zCopyOnWriteList
. Gehen Sie nicht davon aus, dass threadsichere (gleichzeitige) Sammlungen niemals die Ausnahme auslösen können.Collection
können, kann in einigen Fällen unerwartet sein. Ändert beispielsweiseLinkedHashMap.get()
die Sammlung .Programmierung zur Vermeidung gleichzeitiger Änderungsfehler
Beschränken Sie nach Möglichkeit alle Verweise auf ein
Collection
Objekt, damit gleichzeitige Änderungen leichter verhindert werden können. Erstellen SieCollection
einprivate
Objekt oder eine lokale Variable und geben Sie keine Verweise auf dieCollection
oder ihre Iteratoren von Methoden zurück. Es ist dann viel einfacher, alle Stellen zu untersuchen, an denenCollection
Änderungen vorgenommen werden können. Wenn dasCollection
von mehreren Threads verwendet werden soll, ist es praktisch sicherzustellen, dass die ThreadsCollection
nur mit entsprechender Synchronisation und Sperrung auf die Threads zugreifen .quelle
In Java 8 können Sie den Lambda-Ausdruck verwenden:
quelle
Es klingt weniger nach einem Java-Synchronisationsproblem als nach einem Problem mit der Datenbanksperre.
Ich weiß nicht, ob das Hinzufügen einer Version zu all Ihren persistenten Klassen das Problem lösen wird, aber auf diese Weise kann Hibernate exklusiven Zugriff auf Zeilen in einer Tabelle gewähren.
Könnte sein, dass die Isolationsstufe höher sein muss. Wenn Sie "Dirty Reads" zulassen, müssen Sie möglicherweise auf serialisierbar stoßen.
quelle
Probieren Sie entweder CopyOnWriteArrayList oder CopyOnWriteArraySet aus, je nachdem, was Sie versuchen.
quelle
Ich gebe hier nur mein Arbeitsbeispiel für Neulinge, um ihre Zeit zu sparen:
quelle
Ich bin auf diese Ausnahme gestoßen, als ich versucht habe, x letzte Elemente aus der Liste zu entfernen.
myList.subList(lastIndex, myList.size()).clear();
war die einzige Lösung, die für mich funktioniert hat.quelle