Jika saya memiliki dua utas ganda yang mengakses HashMap, tetapi menjamin bahwa mereka tidak akan pernah mengakses kunci yang sama pada saat yang sama, apakah hal itu masih dapat menyebabkan kondisi balapan?
Jika saya memiliki dua utas ganda yang mengakses HashMap, tetapi menjamin bahwa mereka tidak akan pernah mengakses kunci yang sama pada saat yang sama, apakah hal itu masih dapat menyebabkan kondisi balapan?
Jawaban:
Dalam jawaban @ dotsid dia mengatakan ini:
Jika Anda mengubah HashMap dengan cara apa pun, kode Anda akan rusak.
Dia benar. HashMap yang diperbarui tanpa sinkronisasi akan rusak meskipun utas menggunakan sekumpulan kunci yang terputus-putus. Berikut beberapa hal yang bisa salah.
Jika satu thread melakukan a put
, thread lain mungkin melihat nilai usang untuk ukuran hashmap.
Saat utas melakukan tindakan put
yang memicu pembuatan ulang tabel, utas lain mungkin melihat versi sementara atau usang dari referensi larik hashtable, ukurannya, kontennya, atau rantai hash. Kekacauan mungkin terjadi.
Ketika utas melakukan a put
untuk kunci yang bertabrakan dengan beberapa kunci yang digunakan oleh utas lain, dan utas terakhir melakukan a put
untuk kuncinya, maka utas terakhir mungkin melihat salinan usang dari referensi rantai hash. Kekacauan mungkin terjadi.
Ketika satu thread memeriksa tabel dengan kunci yang bertabrakan dengan salah satu kunci thread lainnya, ia mungkin menemukan kunci tersebut pada rantai. Ini akan memanggil sama dengan pada kunci itu, dan jika utas tidak disinkronkan, metode sama mungkin mengalami status basi di kunci itu.
Dan jika Anda memiliki dua utas yang melakukan put
atau remove
meminta secara bersamaan , ada banyak peluang untuk kondisi balapan.
Saya dapat memikirkan tiga solusi:
ConcurrentHashMap
.HashMap
tetapi sinkronkan di luar; misalnya menggunakan mutex primitif, Lock
objek, dan sebagainya.HashMap
untuk setiap utas. Jika utas benar-benar memiliki sekumpulan kunci yang terputus-putus, maka seharusnya tidak perlu (dari perspektif algoritmik) bagi mereka untuk berbagi satu Peta. Memang, jika algoritme Anda melibatkan utas yang mengiterasi kunci, nilai, atau entri peta di beberapa titik, membagi satu peta menjadi beberapa peta dapat memberikan percepatan yang signifikan untuk bagian pemrosesan tersebut.Cukup gunakan ConcurrentHashMap. ConcurrentHashMap menggunakan beberapa kunci yang mencakup berbagai keranjang hash untuk mengurangi kemungkinan penguncian diperebutkan. Ada dampak kinerja marjinal untuk memperoleh kunci yang tidak terbantahkan.
Untuk menjawab pertanyaan awal Anda: Menurut javadoc, selama struktur peta tidak berubah, Anda baik-baik saja. Ini berarti tidak ada elemen yang dihapus sama sekali dan tidak ada penambahan kunci baru yang belum ada di peta. Mengganti nilai yang terkait dengan kunci yang ada tidak masalah.
Jika beberapa utas mengakses peta hash secara bersamaan, dan setidaknya salah satu utas memodifikasi peta secara struktural, peta itu harus disinkronkan secara eksternal. (Modifikasi struktural adalah setiap operasi yang menambah atau menghapus satu atau lebih pemetaan; hanya mengubah nilai yang terkait dengan kunci yang sudah berisi instance bukanlah modifikasi struktural.)
Padahal tidak ada jaminan tentang visibilitas. Jadi, Anda harus bersedia menerima pengambilan asosiasi lama sesekali.
Itu tergantung pada apa yang Anda maksud di bawah "mengakses". Jika Anda hanya membaca, Anda dapat membaca bahkan kunci yang sama selama visibilitas data dijamin di bawah aturan " terjadi sebelum ". Ini berarti HashMap
tidak boleh berubah dan semua perubahan (konstruksi awal) harus diselesaikan sebelum pembaca mulai mengakses HashMap
.
Jika Anda mengubah file HashMap
dengan cara apa pun maka kode Anda rusak. @Stephen C memberikan penjelasan yang sangat bagus mengapa.
EDIT: Jika kasus pertama adalah situasi Anda yang sebenarnya, saya sarankan Anda menggunakan Collections.unmodifiableMap()
untuk memastikan bahwa HashMap Anda tidak pernah berubah. Objek yang dituju HashMap
tidak boleh berubah juga, jadi agresif menggunakanfinal
kata kunci yang dapat membantu Anda.
Dan seperti yang dikatakan @ Lars Andren, ConcurrentHashMap
adalah pilihan terbaik dalam banyak kasus.
unmodifiableMap
memastikan klien tidak dapat mengubah peta. Itu tidak melakukan apa pun untuk memastikan bahwa peta yang mendasarinya tidak berubah.
Memodifikasi HashMap tanpa sinkronisasi yang tepat dari dua utas dapat dengan mudah menyebabkan kondisi balapan.
put()
mengarah ke pengubahan ukuran tabel internal, ini membutuhkan waktu dan utas lainnya terus menulis ke tabel lama.put()
untuk kunci berbeda mengarah ke update bucket yang sama jika kode hash kunci sama modulo ukuran tabel. (Sebenarnya, hubungan antara kode hash dan indeks keranjang lebih rumit, tetapi tabrakan mungkin masih terjadi.)HashMap
implementasi yang Anda gunakan, Anda bisa mendapatkan kerusakan HashMap
struktur data, dan sebagainya yang disebabkan oleh anomali memori.