Implementasi antrian bersamaan mana yang harus saya gunakan di Jawa?

132

Dari JavaDocs:

  • Sebuah ConcurrentLinkedQueue adalah pilihan yang tepat ketika banyak benang akan berbagi akses ke koleksi umum. Antrian ini tidak mengizinkan elemen nol.
  • ArrayBlockingQueue adalah "buffer terbatas" klasik, di mana array berukuran tetap menampung elemen yang dimasukkan oleh produsen dan diekstraksi oleh konsumen. Kelas ini mendukung kebijakan kewajaran opsional untuk memesan utas produsen dan konsumen yang menunggu
  • LinkedBlockingQueue biasanya memiliki throughput yang lebih tinggi daripada antrian berbasis array tetapi kinerja yang kurang dapat diprediksi di sebagian besar aplikasi bersamaan.

Saya punya 2 skenario, satu membutuhkan antrian untuk mendukung banyak produsen (utas menggunakannya) dengan satu konsumen dan yang lainnya adalah sebaliknya.

Saya tidak mengerti implementasi mana yang digunakan. Adakah yang bisa menjelaskan perbedaannya?

Juga, apa 'kebijakan keadilan opsional' di ArrayBlockingQueue?

David Hofmann
sumber
1
Anda lupa bertanya tentang PriorityBlockingQueue juga, yang berguna untuk menentukan pesanan agar utas diproses.
IgorGanapolsky

Jawaban:

53

Pada dasarnya perbedaan di antara mereka adalah karakteristik kinerja dan perilaku memblokir.

Mengambil yang termudah dulu, ArrayBlockingQueueadalah antrian dengan ukuran tetap. Jadi, jika Anda menetapkan ukuran pada 10, dan mencoba untuk memasukkan elemen ke-11, pernyataan menyisipkan akan memblokir sampai utas lain menghapus elemen. Masalah keadilan adalah apa yang terjadi jika beberapa utas mencoba menyisipkan dan menghapus pada saat yang sama (dengan kata lain selama periode ketika Antrian diblokir). Algoritma keadilan memastikan bahwa utas pertama yang meminta adalah utas pertama yang didapat. Jika tidak, utas yang diberikan mungkin menunggu lebih lama dari utas lainnya, menyebabkan perilaku yang tidak dapat diprediksi (kadang-kadang satu utas hanya akan memakan waktu beberapa detik karena utas lain yang mulai kemudian diproses terlebih dahulu). Imbalannya adalah dibutuhkan overhead untuk mengelola keadilan, memperlambat throughput.

Perbedaan paling penting antara LinkedBlockingQueuedan ConcurrentLinkedQueueadalah bahwa jika Anda meminta elemen dari a LinkedBlockingQueuedan antriannya kosong, utas Anda akan menunggu sampai ada sesuatu di sana. A ConcurrentLinkedQueueakan segera kembali dengan perilaku antrian kosong.

Yang mana tergantung pada apakah Anda membutuhkan pemblokiran. Di mana Anda memiliki banyak produsen dan satu konsumen, kedengarannya seperti itu. Di sisi lain, di mana Anda memiliki banyak konsumen dan hanya satu produsen, Anda mungkin tidak memerlukan perilaku pemblokiran, dan mungkin senang jika konsumen memeriksa apakah antriannya kosong dan beralih jika ada.

Yishai
sumber
67
Jawabannya menyesatkan. LinkedBlockingQueue dan ConcurrentLinkedQueue memiliki metode "poll ()" yang menghilangkan kepala antrian atau mengembalikan null (tidak memblokir) dan metode "offer (E e)" yang menyisipkan ke ekor antrian dan tidak memblokir. Perbedaannya adalah bahwa hanya LinkedBlockingQueue yang memiliki operasi pemblokiran selain operasi non-pemblokiran - dan untuk hak istimewa itu, Anda membayar harga yang LinkedBlockingQueue sebenarnya memiliki beberapa penguncian. Jawaban lain menjelaskan ini.
Nakedible
123

ConcurrentLinkedQueue berarti tidak ada kunci yang diambil (yaitu tidak ada panggilan yang disinkronkan (ini) atau Lock.lock ). Ini akan menggunakan operasi CAS - Compare dan Swap selama modifikasi untuk melihat apakah head / tail node masih sama seperti ketika dimulai. Jika demikian, operasi berhasil. Jika simpul kepala / ekor berbeda, ia akan berputar dan mencoba lagi.

LinkedBlockingQueue akan mengunci sebelum ada modifikasi. Jadi panggilan penawaran Anda akan diblokir sampai mereka mendapatkan kunci. Anda dapat menggunakan kelebihan penawaran yang membutuhkan TimeUnit untuk mengatakan Anda hanya bersedia menunggu jumlah X sebelum meninggalkan add (biasanya baik untuk antrian jenis pesan di mana pesan basi setelah X jumlah milidetik).

Keadilan berarti bahwa implementasi Kunci akan menjaga utas tetap teratur. Berarti jika Thread A masuk dan kemudian Thread B masuk, Thread A akan mendapatkan kunci terlebih dahulu. Tanpa keadilan, tidak dapat ditentukan apa yang sebenarnya terjadi. Kemungkinan besar akan menjadi utas berikutnya yang dijadwalkan.

Adapun yang digunakan, itu tergantung. Saya cenderung menggunakan ConcurrentLinkedQueue karena waktu yang dibutuhkan produsen saya untuk mendapatkan pekerjaan untuk dimasukkan ke dalam antrian sangat beragam. Saya tidak memiliki banyak produsen yang memproduksi pada saat yang bersamaan. Tapi sisi konsumen lebih rumit karena jajak pendapat tidak akan masuk ke kondisi tidur yang baik. Anda harus mengatasinya sendiri.

Justin Rudd
sumber
1
Dan dalam kondisi apa ArrayBlockingQueue lebih baik daripada LinkedBlockingQueue?
kolobok
@akapelko ArrayBlockingQueue memungkinkan untuk pemesanan yang lebih halus.
IgorGanapolsky
2
Apa maksudnya doest - "itu akan berputar dan coba lagi." ?
Lester
9

Judul pertanyaan Anda menyebutkan Antrian Pemblokiran. Namun, ConcurrentLinkedQueueini bukan antrian pemblokiran.

The BlockingQueues adalah ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, dan SynchronousQueue.

Beberapa ini adalah jelas tidak cocok untuk tujuan Anda ( DelayQueue, PriorityBlockingQueue, dan SynchronousQueue). LinkedBlockingQueuedan LinkedBlockingDequeidentik, kecuali bahwa yang terakhir adalah Antrian berujung ganda (ini mengimplementasikan antarmuka Deque).

Karena ArrayBlockingQueuehanya berguna jika Anda ingin membatasi jumlah elemen, saya akan tetap melakukannya LinkedBlockingQueue.

Powerlord
sumber
Saya menghapus kata Pemblokiran dari judul, terima kasih. Biarkan saya melihat apakah saya mengerti, apa yang Anda katakan berarti bahwa LinkedBlockingQueue dapat digunakan pada konsumen multil / menghasilkan skenario atas objek yang sama?
David Hofmann
1
Saya pikir ArrayBlockingQueue memungkinkan untuk pemesanan thread yang lebih halus? Karena itu keuntungannya.
IgorGanapolsky
4

ArrayBlockingQueue memiliki jejak memori yang lebih rendah, dapat menggunakan kembali elemen node, tidak seperti LinkedBlockingQueue yang harus membuat objek $ Node LinkedBlockingQueue untuk setiap penyisipan baru.

Deng
sumber
1
poin bagus! saya lebih suka ArrayBlockingQueue lebih dari LinkedBlockingQueue
trillions
2
Ini tidak selalu benar - jika antrian Anda hampir kosong banyak waktu tetapi harus dapat tumbuh besar, ArrayBlockingQueueakan memiliki jejak memori yang jauh lebih buruk - ia masih memiliki array besar yang dialokasikan dalam memori sepanjang waktu, sementara yang LinkedBlockingQueueakan memiliki jejak memori diabaikan ketika dekat dengan kosong.
Krease
1
  1. SynchronousQueue(Diambil dari pertanyaan lain )

SynchronousQueuelebih merupakan handoff, sedangkan yang LinkedBlockingQueueadil memungkinkan satu elemen. Perbedaannya adalah bahwa put()panggilan ke a SynchronousQueuetidak akan kembali sampai ada take()panggilan yang sesuai , tetapi dengan LinkedBlockingQueueukuran 1, put()panggilan (ke antrian kosong) akan segera kembali. Ini pada dasarnya BlockingQueueimplementasi ketika Anda benar-benar tidak ingin antrian (Anda tidak ingin mempertahankan data yang tertunda).

  1. LinkedBlockingQueue( LinkedListImplementasi tetapi Tidak Tepat. Implementasi JDK LinkedListmenggunakan Node kelas internal statis untuk menjaga Link antar elemen)

Konstruktor untuk LinkedBlockingQueue

public LinkedBlockingQueue(int capacity) 
{
        if (capacity < = 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node< E >(null);   // Maintains a underlying linkedlist. ( Use when size is not known )
}

Kelas Node Digunakan untuk Memelihara Tautan

static class Node<E> {
    E item;
    Node<E> next;
    Node(E x) { item = x; }
}

3. ArrayBlockingQueue (Implementasi Array)

Konstruktor untuk ArrayBlockingQueue

public ArrayBlockingQueue(int capacity, boolean fair) 
{
            if (capacity < = 0)
                throw new IllegalArgumentException();
            this.items = new Object[capacity]; // Maintains a underlying array
            lock = new ReentrantLock(fair);
            notEmpty = lock.newCondition();
            notFull =  lock.newCondition();
}

IMHO Perbedaan terbesar antara ArrayBlockingQueuedan LinkedBlockingQueuejelas dari konstruktor satu memiliki struktur data yang mendasari Array dan linkedList lainnya .

ArrayBlockingQueuemenggunakan algoritma kondisi ganda tunggal-kunci dan LinkedBlockingQueuemerupakan varian dari algoritma "dua kunci antrian" dan memiliki 2 kondisi 2 kunci (takeLock, putLock)

Vipin
sumber
0

ConcurrentLinkedQueue bebas kunci, LinkedBlockingQueue tidak. Setiap kali Anda mengaktifkan LinkedBlockingQueue.put () atau LinkedBlockingQueue.take (), Anda harus mendapatkan kunci terlebih dahulu. Dengan kata lain, LinkedBlockingQueue memiliki konkurensi yang buruk. Jika Anda peduli kinerja, coba ConcurrentLinkedQueue + LockSupport.

pengguna319609
sumber