Java'da hangi eşzamanlı Kuyruk uygulamasını kullanmalıyım?


132

JavaDocs'tan:

  • Bir ConcurrentLinkedQueue birçok konu ortak bir koleksiyona erişim paylaşacak uygun bir seçimdir. Bu kuyruk boş öğelere izin vermiyor.
  • ArrayBlockingQueue , sabit boyutlu bir dizinin üreticiler tarafından eklenen ve tüketiciler tarafından çıkarılan öğeleri tuttuğu klasik bir "sınırlı arabellek" dir. Bu sınıf, bekleyen üretici ve tüketici konuları sipariş etmek için isteğe bağlı bir adalet politikasını destekler
  • LinkedBlockingQueue tipik olarak dizi tabanlı kuyruklardan daha yüksek iş hacmine sahiptir, ancak çoğu eşzamanlı uygulamada daha az tahmin edilebilir performansa sahiptir.

2 senaryom var, biri birçok üreticiyi (onu kullanan iş parçacıkları) bir tüketiciyle desteklemek için kuyruğa ihtiyaç duyuyor, diğeri ise diğer yoldur.

Hangi uygulamayı kullanacağımı anlamıyorum. Biri farklılıkların ne olduğunu açıklayabilir mi?

Ayrıca, içindeki 'isteğe bağlı adalet politikası' ArrayBlockingQueuenedir?


1
Ayrıca, iş parçacıklarının işleneceği bir sırayı belirtmek için yararlı olan PriorityBlockingQueue hakkında soru sormayı unuttunuz.
IgorGanapolsky

Yanıtlar:


53

Temel olarak aralarındaki fark, performans özellikleri ve engelleme davranışıdır.

Önce en kolay olanı almak, ArrayBlockingQueuesabit büyüklükte bir kuyruktur. Dolayısıyla, boyutu 10 olarak ayarlarsanız ve 11. öğeyi eklemeye çalışırsanız, insert deyimi başka bir iş parçacığı bir öğeyi kaldırana kadar engellenecektir. Adalet sorunu, aynı anda birden fazla iş parçacığı eklemeye ve çıkarmaya çalışırsa (başka bir deyişle, Kuyruğun engellendiği dönemde) olan şeydir. Bir adalet algoritması, soran ilk iş parçacığının alan ilk iş parçacığı olmasını sağlar. Aksi takdirde, belirli bir iş parçacığı diğer iş parçacıklarından daha uzun süre bekleyerek öngörülemeyen davranışa neden olabilir (bazen bir iş parçacığı birkaç saniye sürebilir çünkü daha sonra başlatılan diğer iş parçacıkları önce işlenir). Değiş tokuş, adaleti yönetmek için ek yük gerektirmesi ve verimi yavaşlatmasıdır.

Arasındaki en önemli fark, LinkedBlockingQueueve ConcurrentLinkedQueuebir gelen bir eleman isteği halinde olmasıdır LinkedBlockingQueueve sırası boş bir şey kalmayıncaya kadar, senin iplik bekleyecektir. A ConcurrentLinkedQueue, boş bir sıranın davranışıyla hemen geri dönecektir.

Engellemeye ihtiyacınız olup olmadığına bağlı. Birçok üreticinin ve bir tüketicinin olduğu yerde, kulağa öyle geliyor. Öte yandan, çok sayıda tüketiciniz ve yalnızca bir üreticiniz olduğunda, engelleme davranışına ihtiyacınız olmayabilir ve tüketicilerin kuyruğun boş olup olmadığını kontrol etmesini ve varsa devam etmesini sağlamaktan memnun olabilirsiniz.


67
Cevap yanıltıcıdır. Hem LinkedBlockingQueue hem de ConcurrentLinkedQueue, kuyruğun başını kaldıran veya boş döndüren (engellemeyen) "anket ()" yöntemine ve kuyruğun kuyruğuna eklenen ve engellemeyen "teklif (E e)" yöntemine sahiptir. Aradaki fark, yalnızca LinkedBlockingQueue'nun engellemeyen işlemlere ek olarak engelleme işlemlerine sahip olmasıdır - ve bu ayrıcalık için LinkedBlockingQueue'nun aslında bir miktar kilitlemeye sahip olduğu fiyatı ödersiniz. Diğer cevap bunu açıklıyor.
Çıplak

123

ConcurrentLinkedQueue , kilit alınmadığı anlamına gelir (yani senkronize edilmiş (bu) veya Lock.lock çağrıları). Baş / kuyruk düğümünün başlatıldığı andakiyle aynı olup olmadığını görmek için değişiklikler sırasında bir CAS - Karşılaştırma ve Değiştirme işlemi kullanacaktır . Eğer öyleyse, operasyon başarılı olur. Baş / kuyruk düğümü farklıysa, dönecek ve tekrar deneyecektir.

LinkedBlockingQueue , herhangi bir değişiklikten önce bir kilit alacaktır. Böylece, teklif çağrılarınız kilitlenene kadar engellenir. Eklemeyi bırakmadan önce yalnızca X kadar süre beklemeye istekli olduğunuzu söylemek için bir TimeUnit alan teklif aşırı yüklemesini kullanabilirsiniz (genellikle mesajın X milisaniyeden sonra eski olduğu mesaj türü kuyrukları için iyidir).

Adillik, Kilit uygulamasının iş parçacıklarını sıralı tutacağı anlamına gelir. Bunun anlamı, İplik A girer ve ardından İplik B girerse, İplik A önce kilidi alır. Adalet olmadan, gerçekte ne olacağı belirsizdir. Büyük olasılıkla planlanan bir sonraki iş parçacığı olacak.

Hangisinin kullanılacağına göre değişir. ConcurrentLinkedQueue kullanma eğilimindeyim çünkü yapımcılarımın kuyruğa koymak için iş bulması gereken süre çok çeşitli. Aynı anda üretim yapan çok fazla üreticim yok. Ancak tüketici tarafı daha karmaşık çünkü anket güzel bir uyku durumuna geçmeyecek. Bunu kendin halletmelisin.


1
Ve hangi koşullar altında ArrayBlockingQueue LinkedBlockingQueue'dan daha iyidir?
kolobok

@akapelko ArrayBlockingQueue, daha hassas bir sıralama sağlar.
IgorGanapolsky

2
Ne anlama geliyor - "dönüp yeniden deneyecek." ?
Lester

9

Soru başlığınızda Engelleme Sıraları geçiyor. Ancak, ConcurrentLinkedQueuebir değil bir engelleme sırası.

BlockingQueueLer vardır ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, ve SynchronousQueue.

Bunlardan bazıları açıkça amaca uygun değildir ( DelayQueue, PriorityBlockingQueueve SynchronousQueue). LinkedBlockingQueueve LinkedBlockingDequeaynıdır, ancak ikincisi çift uçlu bir Kuyruktur (Deque arayüzünü uygular).

Yana ArrayBlockingQueuesen elemanlarının sayısını sınırlamak isterseniz sadece yararlıdır, ben devam ederdim LinkedBlockingQueue.


Engelleme kelimesini başlıktan kaldırdım, teşekkürler. Bakalım anladıysam, söyledikleriniz LinkedBlockingQueue'nun çoklu tüketicilerde kullanılabileceği / aynı nesne üzerinde senaryolar üretebileceği anlamına mı geliyor?
David Hofmann

1
ArrayBlockingQueue'nun iş parçacığı için daha ince taneli sıralamaya izin verdiğini düşündüm. Dolayısıyla avantajı.
IgorGanapolsky

4

ArrayBlockingQueue, daha düşük bellek ayak izine sahiptir, her yeni ekleme için bir LinkedBlockingQueue $ Node nesnesi oluşturmak zorunda olan LinkedBlockingQueue gibi öğe düğümünü yeniden kullanabilir.


1
iyi bir nokta! ArrayBlockingQueue'yu LinkedBlockingQueue'den daha çok tercih ediyorum
trilyonlar

2
Bu mutlaka doğru değildir - kuyruğunuz çoğu zaman boşalmak üzereyse ancak genişlemesi gerekiyorsa, ArrayBlockingQueueçok daha kötü bir bellek ayak izine sahip olacaktır - yine de tüm zaman boyunca bellekte ayrılmış geniş bir diziye sahiptir. LinkedBlockingQueueboşaltmak için zaman yakın ihmal edilebilir bir bellek alanına sahip olacaktır.
Krease

1
  1. SynchronousQueue(Başka bir sorudan alınmıştır )

SynchronousQueuedaha çok bir atlatmadır, oysa LinkedBlockingQueuejust tek bir öğeye izin verir. Aradaki fark, a'ya yapılan put()çağrının, SynchronousQueuekarşılık gelen bir take()çağrı olana kadar geri dönmemesi , ancak LinkedBlockingQueue1 boyutunda, put()çağrı (boş bir kuyruğa) hemen geri dönmesidir. Aslında, BlockingQueuegerçekten bir kuyruk istemediğiniz zamanlar için bir uygulamadır (bekleyen herhangi bir veriyi korumak istemezsiniz).

  1. LinkedBlockingQueue( LinkedListUygulama ama Tam Olarak Değil JDK Uygulaması, LinkedListöğeler arasındaki Bağlantıları korumak için statik iç sınıf Düğümü kullanır)

LinkedBlockingQueue için Oluşturucu

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 )
}

Bağlantıları Korumak İçin Kullanılan Düğüm Sınıfı

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

3. ArrayBlockingQueue (Dizi Uygulaması)

ArrayBlockingQueue için Oluşturucu

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 Array ve diğer linkedList arasındaki en büyük fark ArrayBlockingQueueve kurucudan LinkedBlockingQueueanlaşılır bir veri yapısı vardır .

ArrayBlockingQueuekullanan tek kilit çift durum algoritması ve LinkedBlockingQueue"iki kilit sıra" algoritması varyantıdır ve 2 kilidi 2 koşulları (takeLock, putLock) sahip olan


0

ConcurrentLinkedQueue kilit içermez, LinkedBlockingQueue değildir. LinkedBlockingQueue.put () veya LinkedBlockingQueue.take () 'yi her çağırdığınızda, önce kilidi almanız gerekir. Başka bir deyişle, LinkedBlockingQueue zayıf eşzamanlılığa sahiptir. Performansı önemsiyorsanız ConcurrentLinkedQueue + LockSupport'u deneyin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.