Saya menggunakan Collection
(yang HashMap
digunakan secara tidak langsung oleh JPA, itu terjadi), tetapi ternyata secara acak kode tersebut melempar a ConcurrentModificationException
. Apa yang menyebabkannya dan bagaimana cara memperbaiki masalah ini? Dengan menggunakan sinkronisasi, mungkin?
Ini adalah stack-trace lengkap:
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
sumber
sumber
Jawaban:
Ini bukan masalah sinkronisasi. Ini akan terjadi jika koleksi dasar yang sedang diiterasi diubah oleh apa pun selain Iterator itu sendiri.
Ini akan melempar
ConcurrentModificationException
ketikait.hasNext()
disebut kedua kalinya.Pendekatan yang tepat adalah
Dengan asumsi iterator ini mendukung
remove()
operasi.sumber
Coba gunakan
ConcurrentHashMap
bukan dataranHashMap
sumber
Modifikasi dari
Collection
sementara iterasi melalui ituCollection
menggunakan sebuahIterator
yang tidak diizinkan oleh sebagian besarCollection
kelas. Perpustakaan Java menyebut upaya untuk memodifikasiCollection
sementara iterasi melalui itu sebagai "modifikasi bersamaan". Sayangnya, satu-satunya penyebab yang mungkin adalah modifikasi simultan oleh banyak utas, tetapi tidak demikian. Dengan hanya menggunakan satu utas dimungkinkan untuk membuat iterator untukCollection
(menggunakanCollection.iterator()
, atau loop yang disempurnakanfor
), mulai iterasi (menggunakanIterator.next()
, atau secara setara memasuki tubuhfor
loop yang disempurnakan ), memodifikasiCollection
, lalu melanjutkan iterasi.Untuk membantu programmer, beberapa implementasi dari
Collection
kelas - kelas itu berusaha mendeteksi modifikasi bersamaan yang salah, dan melemparConcurrentModificationException
jika mereka mendeteksinya. Namun, secara umum tidak mungkin dan praktis untuk menjamin deteksi semua modifikasi bersamaan. Jadi penggunaan yang salahCollection
tidak selalu menghasilkan terlemparConcurrentModificationException
.Dokumentasi
ConcurrentModificationException
mengatakan:Catat itu
Dokumentasi
HashSet
,HashMap
,TreeSet
danArrayList
kelas kata ini:Perhatikan lagi bahwa perilaku "tidak dapat dijamin" dan hanya "berdasarkan upaya terbaik".
Dokumentasi beberapa metode
Map
antarmuka mengatakan ini:Perhatikan lagi bahwa hanya "upaya terbaik" yang diperlukan untuk deteksi, dan a
ConcurrentModificationException
secara eksplisit disarankan hanya untuk kelas yang tidak berbarengan (tidak aman untuk benang).Debugging
ConcurrentModificationException
Jadi, ketika Anda melihat tumpukan-jejak karena
ConcurrentModificationException
, Anda tidak dapat langsung berasumsi bahwa penyebabnya adalah akses multi-utas yang tidak aman ke aCollection
. Anda harus memeriksa stack-trace untuk menentukan kelasCollection
melempar pengecualian (metode kelas akan secara langsung atau tidak langsung melemparkannya), dan untukCollection
objek mana . Maka Anda harus memeriksa dari mana objek itu dapat dimodifikasi.Collection
dalamfor
loop yang ditingkatkan di atasCollection
. Hanya karena Anda tidak melihatIterator
objek di kode sumber Anda tidak berarti tidak ada diIterator
sana! Untungnya, salah satu pernyataan darifor
loop yang salah biasanya akan ada di tumpukan-jejak, jadi melacak kesalahan biasanya mudah.Collection
objek. Perhatikan bahwa pandangan koleksi yang tidak dapat dimodifikasi (seperti yang diproduksi olehCollections.unmodifiableList()
) mempertahankan referensi ke koleksi yang dapat dimodifikasi, sehingga iterasi atas koleksi yang "tidak dapat dimodifikasi" dapat membuang pengecualian (modifikasi telah dilakukan di tempat lain). Tampilan lain dari AndaCollection
, seperti sub daftar ,Map
set entri danMap
set kunci juga mempertahankan referensi ke yang asli (dapat dimodifikasi)Collection
. Ini bisa menjadi masalah bahkan untuk thread-safeCollection
, sepertiCopyOnWriteList
; jangan berasumsi bahwa koleksi thread-safe (konkuren) tidak pernah dapat membuang pengecualian.Collection
dapat terjadi secara tak terduga dalam beberapa kasus. Misalnya,LinkedHashMap.get()
memodifikasi koleksinya .Pemrograman untuk mencegah kesalahan modifikasi bersamaan
Jika memungkinkan, batasi semua referensi ke
Collection
objek, jadi lebih mudah untuk mencegah modifikasi bersamaan. MembuatCollection
sebuahprivate
objek atau variabel lokal, dan tidak kembali referensi keCollection
atau iterator yang dari metode. Maka jauh lebih mudah untuk memeriksa semua tempat di manaCollection
dapat dimodifikasi. JikaCollection
akan digunakan oleh banyak utas, maka praktis untuk memastikan bahwa utas mengaksesCollection
hanya dengan sinkronisasi dan penguncian yang sesuai.sumber
Di Java 8, Anda bisa menggunakan ekspresi lambda:
sumber
Itu terdengar kurang seperti masalah sinkronisasi Java dan lebih seperti masalah penguncian basis data.
Saya tidak tahu apakah menambahkan versi ke semua kelas gigih Anda akan mengatasinya, tapi itu salah satu cara Hibernate dapat memberikan akses eksklusif ke baris dalam tabel.
Bisa jadi level isolasi perlu lebih tinggi. Jika Anda mengizinkan "pembacaan kotor", mungkin Anda perlu menambahkan serializable.
sumber
Cobalah CopyOnWriteArrayList atau CopyOnWriteArraySet tergantung pada apa yang Anda coba lakukan.
sumber
Saya hanya memberikan contoh kerja saya di sini untuk pemula untuk menghemat waktu mereka:
sumber
Saya mengalami pengecualian ini ketika mencoba menghapus x item terakhir dari daftar.
myList.subList(lastIndex, myList.size()).clear();
adalah satu-satunya solusi yang berhasil untuk saya.sumber