Używam Collection
(a HashMap
używany pośrednio przez JPA, tak się dzieje), ale najwyraźniej losowo kod rzuca ConcurrentModificationException
. Co to powoduje i jak rozwiązać ten problem? Może używając jakiejś synchronizacji?
Oto pełny ślad stosu:
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
źródło
źródło
Odpowiedzi:
To nie jest problem z synchronizacją. Nastąpi to, jeśli podstawowa kolekcja, po której jest iterowana, zostanie zmodyfikowana przez coś innego niż sam iterator.
Spowoduje to wyświetlenie znaku,
ConcurrentModificationException
gdyit.hasNext()
zostanie wywołany po raz drugi.Właściwe byłoby podejście
Zakładając, że ten iterator obsługuje
remove()
operację.źródło
Spróbuj użyć
ConcurrentHashMap
zamiast zwykłegoHashMap
źródło
Modyfikacja czasu
Collection
podczas iteracji przez toCollection
przy użyciu an nieIterator
jest dozwolona przez większośćCollection
klas. Biblioteka Javy wywołuje próbę zmodyfikowaniaCollection
podczas iteracji przez nią „współbieżną modyfikacją”. To niestety sugeruje, że jedyną możliwą przyczyną jest jednoczesna modyfikacja przez wiele wątków, ale tak nie jest. Używając tylko jednego wątku, można utworzyć iterator dlaCollection
(używającCollection.iterator()
lub rozszerzonejfor
pętli ), rozpocząć iterację (używającIterator.next()
lub równoważnie wprowadzając treść rozszerzonejfor
pętli), zmodyfikowaćCollection
, a następnie kontynuować iterację.Aby pomóc programistom, niektóre implementacje tych
Collection
klas próbują wykryć błędną, współbieżną modyfikację i wyrzucić,ConcurrentModificationException
jeśli ją wykryją. Jednak generalnie nie jest możliwe i praktyczne zagwarantowanie wykrywania wszystkich jednoczesnych modyfikacji. Tak więc błędne użycieCollection
nie zawsze kończy się rzutemConcurrentModificationException
.Dokumentacja
ConcurrentModificationException
mówi:Zwróć na to uwagę
Dokumentacja
HashSet
,HashMap
,TreeSet
iArrayList
zajęcia mówi w ten sposób:Zauważ jeszcze raz, że zachowanie „nie może być zagwarantowane” i jest tylko „na zasadzie najlepszych starań”.
Dokumentacja kilku metod
Map
interfejsu mówi tak:Zwróć uwagę, że do wykrywania wymagana jest tylko „podstawa najlepszego wysiłku”, a a
ConcurrentModificationException
jest wyraźnie sugerowane tylko dla klas innych niż współbieżne (niegwintowane).Debugowanie
ConcurrentModificationException
Tak więc, gdy widzisz ślad stosu związany z a
ConcurrentModificationException
, nie możesz od razu założyć, że przyczyną jest niebezpieczny wielowątkowy dostęp do plikuCollection
. Musisz zbadać ślad stosu, aby określić, która klasaCollection
wyrzuciła wyjątek (metoda klasy bezpośrednio lub pośrednio go wyrzuci) i dla któregoCollection
obiektu. Następnie musisz zbadać, skąd ten obiekt może być modyfikowany.Collection
wewnątrz rozszerzonejfor
pętli naCollection
. To, że nie widziszIterator
obiektu w kodzie źródłowym, nie oznacza, że go nieIterator
ma! Na szczęście jedno z instrukcji błędnejfor
pętli będzie zwykle znajdować się w śladzie stosu, więc śledzenie błędu jest zwykle łatwe.Collection
obiektu. Należy zauważyć, że niemodyfikowalne widoki kolekcji (na przykład utworzone przezCollections.unmodifiableList()
) zachowują odniesienie do modyfikowalnej kolekcji, więc iteracja po kolekcji „niemodyfikowalnej” może zgłosić wyjątek (modyfikacja została przeprowadzona gdzie indziej). Inne poglądy twoichCollection
, takie jak list sub ,Map
zestawy wejścia iMap
najważniejszych zbiorów zachowują również odniesienia do oryginału (modyfikacji)Collection
. Może to stanowić problem nawet dla bezpiecznego wątkuCollection
, takiego jakCopyOnWriteList
; Nie zakładaj, że kolekcje bezpieczne wątkowo (współbieżne) nigdy nie mogą zgłosić wyjątku.Collection
może być w niektórych przypadkach nieoczekiwane. Na przykładLinkedHashMap.get()
modyfikuje swoją kolekcję .Programowanie, aby zapobiec jednoczesnym błędom modyfikacji
Jeśli to możliwe, ogranicz wszystkie odwołania do
Collection
obiektu, aby łatwiej było zapobiec równoczesnym modyfikacjom. DokonaćCollection
doprivate
obiektu lub zmiennej lokalnej, a nie powrót do odwołaniaCollection
lub jego iteratorów z metod. Dużo łatwiej jest wtedy zbadać wszystkie miejsca, w którychCollection
można zmodyfikować. JeśliCollection
ma być używany przez wiele wątków, praktyczne jest upewnienie się, że wątki mają dostępCollection
tylko do tych z odpowiednią synchronizacją i blokowaniem.źródło
W Javie 8 możesz użyć wyrażenia lambda:
źródło
Brzmi to mniej jak problem z synchronizacją Java, a bardziej jak problem z blokowaniem bazy danych.
Nie wiem, czy dodanie wersji do wszystkich twoich trwałych klas rozwiąże ten problem, ale to jeden ze sposobów, w jaki Hibernate może zapewnić wyłączny dostęp do wierszy w tabeli.
Możliwe, że poziom izolacji musi być wyższy. Jeśli zezwolisz na „brudne odczyty”, być może będziesz musiał zwiększyć możliwości serializacji.
źródło
Spróbuj CopyOnWriteArrayList lub CopyOnWriteArraySet w zależności od tego, co próbujesz zrobić.
źródło
Podaję tutaj mój przykład roboczy dla początkujących, aby zaoszczędzić czas:
źródło
Wpadłem na ten wyjątek podczas próby usunięcia x ostatnich pozycji z listy.
myList.subList(lastIndex, myList.size()).clear();
było jedynym rozwiązaniem, które działało dla mnie.źródło