1) Ini CopyOnWriteArraySet
adalah implementasi yang cukup sederhana - pada dasarnya memiliki daftar elemen dalam array, dan ketika mengubah daftar, itu menyalin array. Iterasi dan akses lain yang sedang berjalan saat ini dilanjutkan dengan array lama, menghindari keharusan sinkronisasi antara pembaca dan penulis (meskipun penulisan itu sendiri perlu disinkronkan). Operasi set yang biasanya cepat (terutama contains()
) cukup lambat di sini, karena array akan dicari dalam waktu linier.
Gunakan ini hanya untuk set yang sangat kecil yang akan sering dibaca (diulang) dan jarang diubah. (Swings listener-set akan menjadi contoh, tetapi ini sebenarnya bukan set, dan sebaiknya hanya digunakan dari EDT.)
2) Collections.synchronizedSet
hanya akan membungkus blok tersinkronisasi di sekitar setiap metode set asli. Anda tidak boleh mengakses set asli secara langsung. Ini berarti bahwa tidak ada dua metode set yang dapat dieksekusi secara bersamaan (satu akan memblokir hingga yang lain selesai) - ini aman untuk thread, tetapi Anda tidak akan memiliki konkurensi jika beberapa thread benar-benar menggunakan set. Jika Anda menggunakan iterator, Anda biasanya masih perlu melakukan sinkronisasi secara eksternal untuk menghindari ConcurrentModificationExceptions saat memodifikasi set antara panggilan iterator. Kinerja akan seperti kinerja set asli (tetapi dengan beberapa overhead sinkronisasi, dan pemblokiran jika digunakan secara bersamaan).
Gunakan ini jika Anda hanya memiliki konkurensi rendah, dan ingin memastikan semua perubahan langsung terlihat ke utas lainnya.
3) ConcurrentSkipListSet
adalah SortedSet
implementasi bersamaan , dengan sebagian besar operasi dasar di O (log n). Ini memungkinkan penambahan / penghapusan dan pembacaan / iterasi secara bersamaan, di mana iterasi mungkin atau mungkin tidak memberi tahu tentang perubahan sejak iterator dibuat. Operasi massal hanyalah beberapa panggilan tunggal, dan tidak secara atomis - utas lain hanya dapat mengamati beberapa dari mereka.
Jelas Anda dapat menggunakan ini hanya jika Anda memiliki beberapa urutan total pada elemen Anda. Ini terlihat seperti kandidat ideal untuk situasi konkurensi tinggi, untuk set yang tidak terlalu besar (karena O (log n)).
4) Untuk ConcurrentHashMap
(dan Set yang diturunkan darinya): Di sini sebagian besar opsi dasar adalah (rata-rata, jika Anda memiliki yang baik dan cepat hashCode()
) di O (1) (tetapi mungkin merosot ke O (n)), seperti untuk HashMap / HashSet. Ada konkurensi terbatas untuk penulisan (tabel dipartisi, dan akses tulis akan disinkronkan pada partisi yang diperlukan), sementara akses baca sepenuhnya bersamaan dengan dirinya sendiri dan utas penulisan (tetapi mungkin belum melihat hasil dari perubahan yang saat ini sedang berlangsung. tertulis). Iterator mungkin atau mungkin tidak melihat perubahan sejak dibuat, dan operasi massal tidak bersifat atomik. Mengubah ukuran lambat (seperti untuk HashMap / HashSet), jadi cobalah untuk menghindari ini dengan memperkirakan ukuran yang dibutuhkan saat pembuatan (dan menggunakan sekitar 1/3 lebih dari itu, karena ukurannya berubah ketika 3/4 penuh).
Gunakan ini ketika Anda memiliki set besar, fungsi hash yang baik (dan cepat) dan dapat memperkirakan ukuran set dan konkurensi yang diperlukan sebelum membuat peta.
5) Apakah ada implementasi peta bersamaan lainnya yang dapat digunakan di sini?
ConcurrentHashMap
himpunan berbasis, "jadi cobalah untuk menghindari ini dengan memperkirakan ukuran yang dibutuhkan pada penciptaan." Ukuran yang Anda berikan pada peta harus lebih dari 33% lebih besar dari perkiraan Anda (atau nilai yang diketahui), karena ukuran set berubah pada beban 75%. Saya menggunakanexpectedSize + 4 / 3 + 1
+
dimaksudkan untuk menjadi*
?expectedSize * 4 / 3 + 1
ConcurrentMap
(atauHashMap
) di Java 8 jika jumlah entri yang dipetakan ke keranjang yang sama mencapai nilai ambang batas (saya yakin itu adalah 16) maka daftar tersebut akan diubah menjadi pohon pencarian biner (pohon merah-hitam untuk diutamakan) dan dalam hal ini cari waktu akanO(lg n)
dan tidakO(n)
.Dimungkinkan untuk menggabungkan
contains()
kinerjaHashSet
dengan properti terkait konkurensiCopyOnWriteArraySet
dengan menggunakanAtomicReference<Set>
dan mengganti seluruh set pada setiap modifikasi.Sketsa implementasi:
public abstract class CopyOnWriteSet<E> implements Set<E> { private final AtomicReference<Set<E>> ref; protected CopyOnWriteSet( Collection<? extends E> c ) { ref = new AtomicReference<Set<E>>( new HashSet<E>( c ) ); } @Override public boolean contains( Object o ) { return ref.get().contains( o ); } @Override public boolean add( E e ) { while ( true ) { Set<E> current = ref.get(); if ( current.contains( e ) ) { return false; } Set<E> modified = new HashSet<E>( current ); modified.add( e ); if ( ref.compareAndSet( current, modified ) ) { return true; } } } @Override public boolean remove( Object o ) { while ( true ) { Set<E> current = ref.get(); if ( !current.contains( o ) ) { return false; } Set<E> modified = new HashSet<E>( current ); modified.remove( o ); if ( ref.compareAndSet( current, modified ) ) { return true; } } } }
sumber
AtomicReference
menandai nilai volatile. Ini berarti memastikan tidak ada utas yang membaca data lama dan memberikanhappens-before
jaminan karena kode tidak dapat disusun ulang oleh kompilator. Tetapi jika hanya metode get / setAtomicReference
yang digunakan maka kita sebenarnya menandai variabel kita volatile dengan cara yang mewah.abstract
, tampaknya untuk menghindari keharusan menulis beberapa metode. Saya mulai menambahkannya, tetapi mengalami hambatan denganiterator()
. Saya tidak tahu bagaimana mempertahankan iterator atas benda ini tanpa merusak model. Sepertinya saya selalu harus melaluiref
, dan mungkin mendapatkan set dasar yang berbeda setiap kali, yang membutuhkan iterator baru pada set yang mendasarinya, yang tidak berguna bagi saya, karena akan dimulai dengan item nol. Ada wawasan?while ( true )
perlu di sini?Jika Javadocs tidak membantu, Anda mungkin sebaiknya mencari buku atau artikel untuk membaca tentang struktur data. Sekilas:
sumber
Kumpulan referensi lemah serentak
Perubahan lain adalah serangkaian referensi lemah yang aman untuk thread .
Kumpulan seperti itu berguna untuk melacak pelanggan dalam skenario pub-sub . Saat pelanggan keluar dari ruang lingkup di tempat lain, dan karena itu menuju ke kandidat untuk pengumpulan sampah, pelanggan tidak perlu diganggu dengan berhenti berlangganan dengan anggun. Referensi lemah memungkinkan pelanggan menyelesaikan transisinya untuk menjadi kandidat pengumpulan sampah. Saat sampah akhirnya dikumpulkan, entri dalam kumpulan tersebut dihapus.
Meskipun tidak ada kumpulan tersebut yang secara langsung disediakan dengan kelas yang dibundel, Anda dapat membuatnya dengan beberapa panggilan.
Pertama kita mulai dengan membuat
Set
referensi yang lemah dengan memanfaatkanWeakHashMap
kelas. Ini diperlihatkan dalam dokumentasi kelas untukCollections.newSetFromMap
.Set< YourClassGoesHere > weakHashSet = Collections .newSetFromMap( new WeakHashMap< YourClassGoesHere , Boolean >() ) ;
The Nilai dari peta,
Boolean
, tidak relevan di sini sebagai kunci dari peta membuat kamiSet
.Dalam skenario seperti pub-sub, kita memerlukan keamanan thread jika pelanggan dan penerbit beroperasi pada thread terpisah (kemungkinan besar demikian).
Selangkah lebih maju dengan membungkus sebagai rangkaian yang disinkronkan untuk membuat rangkaian ini aman untuk thread. Masukkan panggilan ke
Collections.synchronizedSet
.this.subscribers = Collections.synchronizedSet( Collections.newSetFromMap( new WeakHashMap <>() // Parameterized types `< YourClassGoesHere , Boolean >` are inferred, no need to specify. ) );
Sekarang kami dapat menambah dan menghapus pelanggan dari hasil kami
Set
. Dan setiap pelanggan yang "menghilang" pada akhirnya akan otomatis dihapus setelah pengumpulan sampah dijalankan. Kapan eksekusi ini terjadi bergantung pada implementasi pengumpul sampah JVM Anda, dan bergantung pada situasi waktu proses saat ini. Untuk diskusi dan contoh kapan dan bagaimana cara mendasariWeakHashMap
membersihkan entri kadaluwarsa, lihat Pertanyaan ini, * Apakah WeakHashMap terus berkembang, atau apakah itu menghapus kunci sampah? * .sumber