Apakah ada Daftar bersamaan di JDK Jawa?

277

Bagaimana saya bisa membuat instance Daftar bersamaan, di mana saya dapat mengakses elemen berdasarkan indeks? Apakah JDK memiliki kelas atau metode pabrik yang dapat saya gunakan?

AlikElzin-kilaka
sumber
28
Kenapa tidak konstruktif? Beberapa usulan CopyOnWriteArrayList yang tidak ditemukan di .Net. Anda dapat mengatakan bahwa kedua pertanyaan berhubungan satu sama lain tetapi tidak untuk menutup yang satu ini !!!
AlikElzin-kilaka
1
Saya tidak tahu mengapa Jarrod Roberson akan berpikir itu adalah ide yang baik untuk mengambil editan terperinci yang dibuat oleh Stephan dan mengembalikannya ke pertanyaan awal yang tidak diucapkan dengan baik. Jawaban Jarrod masih bisa diterima. Bahkan, CopyOnWriteArrayList adalah satu-satunya kelas yang mengimplementasikan Daftar bersamaan di JDK. Bingung ...
Matt Passell
10
Karena jawaban yang diterima adalah pertanyaan asli dan Stephan mengajukan pertanyaan yang sama sekali tidak berhubungan dengan sekelompok kode sumber yang tidak dimasukkan oleh pengirim asli di mana pun untuk mengubah pertanyaan sepenuhnya, yang menghasilkan lebih banyak jawaban yang menyarankan hal-hal selain Listyang aslinya khusus mengatakan adalah persyaratan yang dianggap vandalisme. Seorang moderator sudah mengunci pertanyaan karena orang-orang yang mengeluh bahwa jawaban tidak menjawab versi pertanyaan yang dirusak itu.
1
/ locked/ closed/ komentar sebelumnya
8
Tidak ada alasan untuk pertanyaan ini ditutup. Ia bertanya tentang kelas di JDK, yang tidak seperti mencari perpustakaan; itu adalah pangkalan Jawa.
maaartinus

Jawaban:

175

Ada implementasi daftar bersamaan di java.util.concurrent . CopyOnWriteArrayList pada khususnya.

Basil Bourque
sumber
84
Catatan itu menyalin seluruh daftar pada setiap sisipan, sehingga seringkali tidak efisien.
dfrankow
22
@dfrankow Tapi itu bisa lebih lebih efisien jika Anda iterasi lebih dari yang Anda memperbarui Anda.
b1nary.atr0phy
Tidak berfungsi dengan baik seperti yang ditunjukkan di sini. Saya memiliki pengecualian meskipun saya hanya menggunakan metode addAll dan membacanya menggunakan aliran. stackoverflow.com/questions/1527519/…
devssh
166

Jika Anda tidak peduli tentang memiliki akses berbasis indeks dan hanya ingin karakteristik pelestarian urutan penyisipan Daftar, Anda dapat mempertimbangkan java.util.concurrent.ConcurrentLinkedQueue . Karena mengimplementasikan Iterable, setelah Anda selesai menambahkan semua item, Anda dapat mengulangi konten menggunakan sintaks yang ditingkatkan untuk:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}
Matt Passell
sumber
4
Saya pikir yang disederhanakan untuk statement ( :) disebut foreach: docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka
2
@ AlikElzin-kilaka Anda benar. Saya pikir nama itu selalu mengganggu saya, karena sintaks yang sebenarnya tidak menyertakan kata "masing-masing", tetapi saya akan memperbarui jawaban untuk menggunakan nama resmi. :)
Matt Passell
3
@ AlikElzin-kilaka Nitpicking, tetapi menurut JLS versi 8 itu disebut "ditingkatkan untuk pernyataan". Hal yang sama di tutorial java .
Roland
1
@Roland jelas BUKAN nitpicking. Ada (sekarang) perbedaan antara "untuk masing-masing" dan "ditingkatkan untuk" di Jawa.
hfontanez
2
@Rand secara tidak langsung. Saya percaya mereka mengganti nama "untuk setiap loop" menjadi "ditingkatkan untuk" untuk menghilangkan kebingungan antara Stream.forEach dan apa yang sekarang dikenal sebagai ditingkatkan untuk.
hfontanez
128

Anda dapat menggunakan Collections.synchronizedList (Daftar) dengan sangat baik jika yang Anda butuhkan adalah sinkronisasi doa sederhana:

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());
Yanick Rochon
sumber
70
Hasil dari synchronizedList"disinkronkan" tetapi tidak "bersamaan". Satu masalah mendasar yang banyak operasi Daftar - yang berbasis indeks - itu sendiri tidak atomik dan perlu menjadi bagian dari konstruksi pengecualian bersama yang lebih besar.
6
IMO, asing Vectorlebih mudah daripada Collections.synchronizedList(new ArrayList<Object>()).
Stephan
8
Hasil dari sinkronisasi daftar "disinkronkan" tetapi tidak "bersamaan".
Kanagavelu Sugumar
46

Karena tindakan mendapatkan posisi dan mendapatkan elemen dari posisi yang diberikan secara alami memerlukan penguncian (Anda tidak dapat membuat daftar memiliki perubahan struktural antara kedua operasi).

Ide dari kumpulan konkuren adalah bahwa setiap operasi sendiri adalah atomik dan dapat dilakukan tanpa penguncian / sinkronisasi eksplisit.

Oleh karena itu mendapatkan elemen pada posisi ndari Listsuatu operasi atom tidak masuk akal dalam situasi di mana akses bersamaan diantisipasi.

Joachim Sauer
sumber
6
Joachim, saya pikir Anda memukul paku di kepala. Ambil contoh, daftar hanya-baca sebagai daftar bersamaan. Mendapatkan elemen pada posisi N dari daftar tidak hanya masuk akal, tetapi itu adalah singkatnya masalah. Jadi, daftar yang tidak berubah (huruf kecil L) akan menjadi contoh yang baik, tetapi itu bukan Daftar (huruf besar L). CopyOnWriteArrayList bersamaan, tetapi banyak orang tidak menyukai kinerjanya. Sebuah solusi di sepanjang garis tali (tali tali) mungkin akan menjadi pemenang yang baik.
johnstosh
1
Poin yang sangat bagus. Tetapi Daftar yang akan digunakan OP mungkin memiliki penggunaan yang sangat spesifik. Misalnya mungkin diisi dalam lingkungan bersamaan, lalu "dikunci" (apa pun artinya) dan kemudian diakses dengan aman oleh indeks. Jadi, pada tahap pertama pengisian Daftar seperti itu masih akan membutuhkan implementasi thread-safe. Sayangnya, OP tidak spesifik tentang bagaimana Daftar yang ia cari akan digunakan.
igor.zh
13

Anda memiliki opsi ini:

  • Collections.synchronizedList(): Anda dapat membungkus Listimplementasi apa pun ( ArrayList, LinkedListatau daftar pihak ketiga). Akses ke setiap metode (membaca dan menulis) akan dilindungi menggunakan synchronized. Saat menggunakan iterator()atau ditingkatkan untuk loop, Anda harus melakukan sinkronisasi secara manual; saat iterasi, utas lainnya sepenuhnya diblokir bahkan dari membaca. Anda juga dapat melakukan sinkronisasi secara terpisah untuk masing-masing hasNextdan nextpanggilan, tetapi kemudian ConcurrentModificationExceptiondimungkinkan.

  • CopyOnWriteArrayList: mahal untuk dimodifikasi, tapi tunggu untuk dibaca. Iterator tidak pernah membuang ConcurrentModificationException, mereka mengembalikan snapshot dari daftar pada saat pembuatan iterator, bahkan jika daftar tersebut diubah oleh utas lain saat iterasi. Berguna untuk daftar yang jarang diperbarui. Operasi massal seperti addAlllebih disukai untuk pembaruan - larik internal disalin lebih sedikit.

  • Vector: sangat mirip synchronizedList, tapi iterasi juga disinkronkan. Namun, iterator dapat melempar ConcurrentModificationException, jika vektor dimodifikasi oleh utas lainnya saat iterasi.

Pilihan lain:

  • Collections.unmodifiableList(): bebas kunci, aman dari benang, tetapi tidak dapat dimodifikasi
  • Queueatau Dequemungkin menjadi alternatif jika Anda hanya menambah / menghapus di ujung daftar dan beralih daftar. Tidak ada akses menurut indeks dan tidak ada penambahan / penghapusan di tempat sewenang-wenang. Mereka memiliki beberapa implementasi konkuren dengan kinerja yang lebih baik dan akses konkuren yang lebih baik, tetapi itu di luar cakupan pertanyaan ini. Anda juga dapat melihat JCTools , mereka mengandung lebih banyak implementasi antrian yang dikhususkan untuk konsumen tunggal atau produsen tunggal.
Oliv
sumber
4

masukkan deskripsi gambar di sini

CopyOnWriteArrayList adalah varian thread-safe dari ArrayList di mana semua operasi mutatif (tambah, set, dan sebagainya) diimplementasikan dengan membuat salinan baru dari array yang mendasarinya.

CopyOnWriteArrayList adalah alternatif berbarengan dari antarmuka Daftar alat yang disinkronkan Daftar yang disinkronkan dan bagiannya dari paket java.util.concurrent dan koleksi yang aman.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList gagal-aman dan tidak membuang ConcurrentModificationException ketika mendasari CopyOnWriteArrayList dimodifikasi selama Iterasi menggunakan salinan ArrayList yang terpisah.

Ini biasanya terlalu mahal karena array salin melibatkan setiap operasi pembaruan salinan kloning akan dibuat. CopyOnWriteArrayList adalah pilihan terbaik hanya untuk operasi baca yang sering.

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
vaquar khan
sumber