Modifikasi dari Collection
sementara iterasi melalui itu Collection
menggunakan sebuah Iterator
yang tidak diizinkan oleh sebagian besar Collection
kelas. Perpustakaan Java menyebut upaya untuk memodifikasi Collection
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 untuk Collection
(menggunakan Collection.iterator()
, atau loop yang disempurnakanfor
), mulai iterasi (menggunakan Iterator.next()
, atau secara setara memasuki tubuh for
loop yang disempurnakan ), memodifikasi Collection
, lalu melanjutkan iterasi.
Untuk membantu programmer, beberapa implementasi dari Collection
kelas - kelas itu berusaha mendeteksi modifikasi bersamaan yang salah, dan melempar ConcurrentModificationException
jika mereka mendeteksinya. Namun, secara umum tidak mungkin dan praktis untuk menjamin deteksi semua modifikasi bersamaan. Jadi penggunaan yang salah Collection
tidak selalu menghasilkan terlempar ConcurrentModificationException
.
Dokumentasi ConcurrentModificationException
mengatakan:
Pengecualian ini dapat dilemparkan oleh metode yang telah mendeteksi modifikasi bersamaan dari suatu objek ketika modifikasi tersebut tidak diizinkan ...
Perhatikan bahwa pengecualian ini tidak selalu menunjukkan bahwa suatu objek telah diubah secara bersamaan oleh utas yang berbeda. Jika satu utas mengeluarkan urutan pemanggilan metode yang melanggar kontrak suatu objek, objek tersebut dapat membuang pengecualian ini ...
Perhatikan bahwa perilaku gagal-cepat tidak dapat dijamin karena, secara umum, tidak mungkin untuk membuat jaminan keras di hadapan modifikasi bersamaan yang tidak disinkronkan. Operasi gagal-cepat ConcurrentModificationException
berdasarkan upaya terbaik.
Catat itu
Dokumentasi HashSet
, HashMap
, TreeSet
dan ArrayList
kelas kata ini:
Iterator yang dikembalikan [langsung atau tidak langsung dari kelas ini] gagal-cepat: jika [koleksi] dimodifikasi kapan saja setelah iterator dibuat, dengan cara apa pun kecuali melalui metode hapus iterator sendiri, Iterator
lemparan a ConcurrentModificationException
. Dengan demikian, dalam menghadapi modifikasi bersamaan, iterator gagal dengan cepat dan bersih, daripada mengambil risiko perilaku non-deterministik yang sewenang-wenang pada waktu yang tidak ditentukan di masa depan.
Perhatikan bahwa perilaku gagal-cepat dari iterator tidak dapat dijamin karena, secara umum, tidak mungkin untuk membuat jaminan keras di hadapan modifikasi bersamaan yang tidak disinkronkan. Iterator gagal-cepat melemparkan ConcurrentModificationException
pada upaya terbaik. Oleh karena itu, akan salah untuk menulis sebuah program yang bergantung pada pengecualian ini untuk kebenarannya: perilaku iterator yang gagal cepat harus digunakan hanya untuk mendeteksi bug .
Perhatikan lagi bahwa perilaku "tidak dapat dijamin" dan hanya "berdasarkan upaya terbaik".
Dokumentasi beberapa metode Map
antarmuka mengatakan ini:
Implementasi non-konkuren harus menimpa metode ini dan, berdasarkan upaya terbaik, melempar ConcurrentModificationException
jika terdeteksi bahwa fungsi pemetaan memodifikasi peta ini selama perhitungan. Implementasi bersamaan harus mengesampingkan metode ini dan, atas dasar upaya terbaik, melempar IllegalStateException
jika terdeteksi bahwa fungsi pemetaan memodifikasi peta ini selama perhitungan dan akibatnya perhitungan tidak akan pernah selesai.
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 a Collection
. Anda harus memeriksa stack-trace untuk menentukan kelas Collection
melempar pengecualian (metode kelas akan secara langsung atau tidak langsung melemparkannya), dan untuk Collection
objek mana . Maka Anda harus memeriksa dari mana objek itu dapat dimodifikasi.
- Penyebab paling umum adalah modifikasi
Collection
dalam for
loop yang ditingkatkan di atas Collection
. Hanya karena Anda tidak melihat Iterator
objek di kode sumber Anda tidak berarti tidak ada di Iterator
sana! Untungnya, salah satu pernyataan dari for
loop yang salah biasanya akan ada di tumpukan-jejak, jadi melacak kesalahan biasanya mudah.
- Kasus rumit adalah ketika kode Anda berkeliling referensi ke
Collection
objek. Perhatikan bahwa pandangan koleksi yang tidak dapat dimodifikasi (seperti yang diproduksi oleh Collections.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 Anda Collection
, seperti sub daftar , Map
set entri dan Map
set kunci juga mempertahankan referensi ke yang asli (dapat dimodifikasi) Collection
. Ini bisa menjadi masalah bahkan untuk thread-safe Collection
, seperti CopyOnWriteList
; jangan berasumsi bahwa koleksi thread-safe (konkuren) tidak pernah dapat membuang pengecualian.
- Operasi mana yang dapat dimodifikasi
Collection
dapat terjadi secara tak terduga dalam beberapa kasus. Misalnya, LinkedHashMap.get()
memodifikasi koleksinya .
- Kasus-kasus yang paling sulit adalah ketika pengecualian adalah karena modifikasi bersamaan oleh beberapa thread.
Pemrograman untuk mencegah kesalahan modifikasi bersamaan
Jika memungkinkan, batasi semua referensi ke Collection
objek, jadi lebih mudah untuk mencegah modifikasi bersamaan. Membuat Collection
sebuah private
objek atau variabel lokal, dan tidak kembali referensi ke Collection
atau iterator yang dari metode. Maka jauh lebih mudah untuk memeriksa semua tempat di mana Collection
dapat dimodifikasi. Jika Collection
akan digunakan oleh banyak utas, maka praktis untuk memastikan bahwa utas mengakses Collection
hanya dengan sinkronisasi dan penguncian yang sesuai.