ConcurrentModificationException neden atılır ve nasıl hata ayıklanır?


130

Bir kullanıyorum Collection( HashMapJPA tarafından dolaylı olarak kullanılıyor, öyle oluyor), ama görünüşe göre rastgele bir şekilde kod bir ConcurrentModificationException. Buna ne sebep oluyor ve bu sorunu nasıl düzeltirim? Belki biraz senkronizasyon kullanarak?

İşte tam yığın izleme:

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)

1
Biraz daha içerik sağlayabilir misin? Bir varlığı birleştiriyor, güncelliyor veya siliyor musunuz? Bu kuruluş hangi ilişkilere sahip? Basamaklı ayarlarınız ne olacak?
ordnungswidrig

1
Yığın izlemesinden, Özel Durumun HashMap üzerinden yinelenirken gerçekleştiğini görebilirsiniz. Elbette başka bir iş parçacığı haritayı değiştiriyor, ancak istisna yinelenen iş parçacığında ortaya çıkıyor.
Chochos

Yanıtlar:


263

Bu bir senkronizasyon sorunu değildir. Bu, yinelenen temel koleksiyon, Yineleyicinin kendisinden başka herhangi bir şey tarafından değiştirilirse gerçekleşir.

Iterator it = map.entrySet().iterator();
while (it.hasNext())
{
   Entry item = it.next();
   map.remove(item.getKey());
}

Bu atacağım ConcurrentModificationExceptionzaman it.hasNext()ikinci kez denir.

Doğru yaklaşım

   Iterator it = map.entrySet().iterator();
   while (it.hasNext())
   {
      Entry item = it.next();
      it.remove();
   }

Bu yineleyicinin işlemi desteklediğini varsayarsak remove().


1
Muhtemelen, ancak Hibernate yinelemeyi yapıyor gibi görünüyor, bu makul şekilde doğru bir şekilde uygulanması gerekiyor. Haritayı değiştiren geri arama olabilir, ancak bu olası değildir. Tahmin edilemezlik, gerçek bir eşzamanlılık sorununa işaret ediyor.
Tom Hawtin - tackline

Bu istisnanın iş parçacığı eşzamanlılığı ile ilgisi yoktur, değiştirilen yineleyicinin destek deposundan kaynaklanır. Yineleyici için başka bir iş parçacığı tarafından önemli değil. IMHO, nedeni hakkında yanlış bir izlenim bıraktığı için kötü adlandırılmış bir istisnadır.
Robin

Bununla birlikte, eğer tahmin edilemezse, büyük olasılıkla bu istisna koşullarının oluşmasına neden olan bir iş parçacığı sorunu olduğunu kabul ediyorum. Bu da istisna adı nedeniyle her şeyi daha kafa karıştırıcı hale getiriyor.
Robin

Bu doğru ve kabul edilen cevaptan daha iyi bir açıklamadır, ancak kabul edilen cevap güzel bir çözümdür. ConcurrentHashMap, bir yineleyici içinde bile CME'ye tabi değildir (yineleyici hala tek iş parçacıklı erişim için tasarlanmış olsa da).
G__

Bu çözümün bir anlamı yok, çünkü Haritalar'da iterator () yöntemi yok. Robin'in örneği, örneğin Listeler için geçerli olabilir.
peter

72

ConcurrentHashMapDüz yerine a kullanmayı deneyinHashMap


Bu gerçekten sorunu çözdü mü? Ben de aynı sorunu yaşıyorum, ancak herhangi bir iş parçacığı sorununu kesinlikle ekarte edebilirim.
tobiasbayer

5
Diğer bir çözüm, haritanın bir kopyasını oluşturmak ve bunun yerine bu kopyayı yinelemektir. Veya anahtar kümesini kopyalayın ve her bir anahtarın değerini orijinal haritadan alarak bunları yineleyin.
Chochos

Koleksiyon boyunca yineleyen kişi Hazırda Bekletme olduğundan, basitçe kopyalayamazsınız.
tobiasbayer

1
Anında kurtarıcı. Bunun neden bu kadar iyi çalıştığına bakacağım, böylece daha fazla sürprizle karşılaşmayacağım.
Valchris

1
Sanırım senkronizasyon sorunu değil, aynı nesneyi döngüye alırken aynı modifikasyonun değiştirilmesi sorun olur.
Rais Alam

17

Bir Modifikasyonu Collectioniterating ise o aracılığıyla Collectionbir kullanarak Iteratoredilir izin verilmez çoğu tarafından Collectionsınıfları. Java kitaplığı, bir Collectionsüreyi yineleyerek bir "eşzamanlı değişiklik" değiştirme girişimini çağırır . Bu maalesef olası tek nedenin birden çok iş parçacığı tarafından eşzamanlı olarak değiştirildiğini gösteriyor, ancak bu öyle değil. Yalnızca bir iş parçacığı Collectionkullanarak Collection.iterator(), (kullanarak veya gelişmiş bir fordöngü kullanarak) için bir yineleyici oluşturmak , yinelemeye başlamak (kullanarak Iterator.next()veya gelişmiş fordöngü gövdesine eşdeğer bir şekilde girmek ), öğesini değiştirmek ve Collectionardından yinelemeye devam etmek mümkündür.

Programcılara yardımcı olmak için , bu sınıfların bazı uygulamaları, hatalı eşzamanlı değişiklikleri tespit etmeye ve bunu tespit ederse bir atmaya çalışır. Bununla birlikte, eşzamanlı tüm değişikliklerin tespitini garanti etmek genellikle mümkün ve pratik değildir. Bu yüzden yanlış kullanımı her zaman bir fırlatmaya neden olmaz .CollectionConcurrentModificationExceptionCollectionConcurrentModificationException

Belgeleri ConcurrentModificationExceptiondiyor ki:

Bu istisna, bu tür bir değişikliğe izin verilmezse, bir nesnenin eşzamanlı olarak değiştirilmesini tespit eden yöntemler tarafından atılabilir ...

Bu istisnanın her zaman bir nesnenin farklı bir iş parçacığı tarafından eşzamanlı olarak değiştirildiğini göstermediğini unutmayın. Tek bir iş parçacığı, bir nesnenin sözleşmesini ihlal eden bir dizi yöntem çağrısı yayınlarsa, nesne bu istisnayı atabilir ...

Eşzamanlı olmayan eşzamanlı değişikliklerin varlığında herhangi bir kesin garanti vermenin imkansız olduğu için başarısızlık hızlı davranışının garanti edilemeyeceğini unutmayın. Başarısızlık hızlı operasyonlar ConcurrentModificationException, en iyi çabayı temel alır.

Bunu not et

Dokümantasyonu HashSet, HashMap, TreeSetve ArrayListsınıfları bu diyor ki:

[Bu sınıftan doğrudan veya dolaylı olarak] döndürülen yineleyiciler başarısız olur: [koleksiyon], yineleyici oluşturulduktan sonra herhangi bir zamanda değiştirilirse, yineleyicinin kendi kaldırma yöntemi dışında herhangi bir şekilde, Iteratoratar a ConcurrentModificationException. Bu nedenle, eşzamanlı değişiklik karşısında yineleyici, gelecekte belirsiz bir zamanda keyfi, deterministik olmayan davranışları riske atmak yerine, hızlı ve temiz bir şekilde başarısız olur.

Eşzamanlı olmayan eşzamanlı değişikliklerin varlığında herhangi bir kesin garanti vermenin imkansız olduğu için, bir yineleyicinin başarısızlık hızlı davranışının garanti edilemeyeceğine dikkat edin. Başarısızlık hızlı yineleyiciler ConcurrentModificationException, en iyi çabayı temel alır. Bu nedenle, doğruluğu için bu istisnaya dayanan bir program yazmak yanlış olur : Yineleyicilerin hata-hızlı davranışı yalnızca hataları tespit etmek için kullanılmalıdır .

Davranışın "garanti edilemeyeceğini" ve yalnızca "en iyi çaba temelinde" olduğunu tekrar unutmayın.

MapArayüzün çeşitli yöntemlerinin dokümantasyonu şunu söylüyor:

Eşzamanlı olmayan uygulamalar bu yöntemi geçersiz kılmalıdır ve en iyi çaba temelinde, ConcurrentModificationExceptioneşleme işlevinin hesaplama sırasında bu haritayı değiştirdiği tespit edilirse bir a atmalıdır. Eşzamanlı uygulamalar bu yöntemi geçersiz kılmalı ve en iyi çaba temelinde, IllegalStateExceptioneşleme işlevinin hesaplama sırasında bu haritayı değiştirdiği ve sonuç olarak hesaplamanın asla tamamlanmayacağı tespit edilirse bir atılmalıdır .

Yine, algılama için yalnızca bir "en iyi çaba temeli" gerektiğini ve ConcurrentModificationExceptiona'nın yalnızca eşzamanlı olmayan (iş parçacığı güvenli olmayan) sınıflar için açıkça önerildiğini unutmayın.

Hata ayıklama ConcurrentModificationException

Bu nedenle, a nedeniyle bir yığın izleme ConcurrentModificationExceptiongördüğünüzde, nedeninin a'ya güvenli olmayan çok iş parçacıklı erişim olduğunu hemen varsayamazsınız Collection. Eğer gerekir yığın izlemesi incelemek hangi sınıfı belirlemek için Collectionözel durum (sınıfının bir yöntem doğrudan veya dolaylı olarak atılan sahip olur), ve bunun için Collectionnesne. Ardından, bu nesnenin nereden değiştirilebileceğini incelemelisiniz.

  • En yaygın nedeni modifikasyonu olan Collectiongelişmiş bir mesafede forfazla döngü Collection. IteratorKaynak kodunuzda bir nesne görmemeniz, orada olmadığı anlamına gelmez Iterator! Neyse ki, hatalı fordöngünün ifadelerinden biri genellikle yığın izlemede olacaktır, bu nedenle hatayı bulmak genellikle kolaydır.
  • Daha zor bir durum, kodunuzun Collectionnesneye yapılan referansların etrafından geçmesidir. O Not unmodifiable (ürettiği gibi koleksiyonların görünümleri Collections.unmodifiableList()böylece), değiştirilebilir koleksiyonuna bir başvuru korumak bir "unmodifiable" koleksiyonu istisnası atabilir üzerinde yineleme (modifikasyon başka yerde yapılmıştır). Diğer görünümler , aramalarınızdan Collectiongibi alt listeleri , Mapgiriş setleri ve Mapanahtar setleri de orijinal (değiştirilebilir) başvurular korumak Collection. Bu, iş parçacığı için güvenli olanlar için bile bir sorun olabilir Collection, örneğin CopyOnWriteList; iş parçacığı açısından güvenli (eşzamanlı) koleksiyonların hiçbir zaman istisna atamayacağını varsaymayın.
  • Hangi işlemlerin a'yı değiştirebileceği Collectionbazı durumlarda beklenmedik olabilir. Örneğin, LinkedHashMap.get()koleksiyonunu değiştirir .
  • En zor durumlar, istisnanın birden çok iş parçacığı tarafından eşzamanlı değişikliklere bağlı olduğu durumlardır .

Eşzamanlı değişiklik hatalarını önlemek için programlama

Mümkün olduğunda, tüm başvuruları bir Collectionnesneye sınırlayın , böylece eşzamanlı değişiklikleri önlemek daha kolay olur. Make Collectiona privatenesne ya da yerel bir değişken ve başvurular dönmüyor Collectionyöntemleri veya Yineleyicilerin. Daha sonra değiştirilebilecek tüm yerleri incelemek çok daha kolaydır Collection. Eğer Collectionbirden fazla iş parçacığı tarafından kullanılacaksa, dişlilerin Collectionyalnızca uygun senkronizasyon ve kilitleme ile erişmesini sağlamak pratiktir .


Tek bir iş parçacığı durumunda neden eşzamanlı değişikliğe izin verilmediğini merak ediyorum. Tek bir iş parçacığının normal bir karma haritada eşzamanlı değişiklik yapmasına izin verilirse ne tür sorunlar ortaya çıkabilir?
MasterJoe

4

Java 8'de lambda ifadesini kullanabilirsiniz:

map.keySet().removeIf(key -> key condition);

2

Bir Java senkronizasyon sorunu gibi görünmüyor ve daha çok bir veritabanı kilitleme sorunu gibi geliyor.

Tüm kalıcı sınıflarınıza bir sürüm eklemenin bunu çözüp çözmeyeceğini bilmiyorum, ancak bu, Hibernate'in bir tablodaki satırlara özel erişim sağlayabilmesinin bir yoludur.

Bu izolasyon seviyesinin daha yüksek olması gerekebilir. "Kirli okumalara" izin verirseniz, belki de seri hale getirilebilir hale getirmeniz gerekebilir.


Hashtable demek istediklerini düşünüyorum. JDK 1.0'ın bir parçası olarak gönderildi. Vector gibi, iş parçacığı için güvenli ve yavaş olacak şekilde yazılmıştır. Her ikisi de iş parçacığı olmayan güvenli alternatiflerle değiştirildi: HashMap ve ArrayList. Kullandığınız kadar ödeyin.
duffymo

0

Ne yapmaya çalıştığınıza bağlı olarak CopyOnWriteArrayList veya CopyOnWriteArraySet'i deneyin.


0

Aynı benim gibi haritayı yinelerken haritadan bazı girişleri kaldırmaya çalışıyorsanız, seçilen cevabın, bazı değişikliklerden önce bağlamınıza doğrudan uygulanamayacağını unutmayın.

Yeni başlayanların zamanlarını kurtarmaları için burada çalışma örneğimi veriyorum:

HashMap<Character,Integer> map=new HashMap();
//adding some entries to the map
...
int threshold;
//initialize the threshold
...
Iterator it=map.entrySet().iterator();
while(it.hasNext()){
    Map.Entry<Character,Integer> item=(Map.Entry<Character,Integer>)it.next();
    //it.remove() will delete the item from the map
    if((Integer)item.getValue()<threshold){
        it.remove();
    }

0

Listeden son x öğeyi kaldırmaya çalışırken bu istisnayla karşılaştım. myList.subList(lastIndex, myList.size()).clear();benim için işe yarayan tek çözümdü.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.