Moje pytanie dotyczy tego pytania zadanego wcześniej. W sytuacjach, w których używam kolejki do komunikacji między wątkami producenta i konsumenta, czy ludzie ogólnie zalecają używanie LinkedBlockingQueue
lub ConcurrentLinkedQueue
?
Jakie są zalety / wady używania jednego nad drugim?
Główną różnicą, którą widzę z perspektywy API, jest to, że LinkedBlockingQueue
może być opcjonalnie ograniczony.
źródło
ConcurrentLinkedQueue
jest również przydatne, jeśli Twój wątek sprawdza wiele kolejek. Na przykład na serwerze z wieloma dzierżawcami. Zakładając, że z powodów izolacji nie używasz zamiast tego pojedynczej kolejki blokowania i dyskryminatora dzierżawy.take()
iput()
tylko zużywa dodatkowe zasoby (pośrednie synchronizacji) niżConcurrentLinkedQueue
. chociaż tak jest w przypadku stosowania kolejek ograniczonych w scenariuszach producent-konsumentTo pytanie zasługuje na lepszą odpowiedź.
Java
ConcurrentLinkedQueue
jest oparta na słynnym algorytmie Mageda M. Michaela i Michaela L. Scotta, który zapewnia brak blokowania bez blokowania kolejek .„Nieblokujący” jako termin określający rywalizujący zasób (nasza kolejka) oznacza, że niezależnie od tego, co robi program planujący platformy, np. Przerywa wątek, lub jeśli dany wątek jest po prostu zbyt wolny, inne wątki rywalizują o ten sam zasób nadal będzie mógł się rozwijać. Jeśli na przykład występuje blokada, wątek utrzymujący blokadę może zostać przerwany, a wszystkie wątki oczekujące na tę blokadę zostaną zablokowane. Wewnętrzne blokady (
synchronized
słowo kluczowe) w Javie mogą również wiązać się z poważnymi spadkami wydajności - na przykład w przypadku blokowania stronniczegojest zaangażowany i masz rywalizację lub po tym, jak maszyna wirtualna zdecyduje się „nadmuchać” blokadę po okresie karencji obrotu i zablokować rywalizujące wątki ... dlatego w wielu kontekstach (scenariusze niskiej / średniej rywalizacji) wykonując porównania i -sets na atomowych referencjach może być znacznie bardziej wydajne i właśnie to robi wiele nieblokujących struktur danych.Java
ConcurrentLinkedQueue
jest nie tylko nieblokująca, ale ma niesamowitą właściwość polegającą na tym, że producent nie walczy z konsumentem. W scenariuszu z jednym producentem / pojedynczym konsumentem (SPSC) oznacza to naprawdę, że nie będzie o czym mówić. W scenariuszu z wieloma producentami / jednym konsumentem konsument nie będzie walczył z producentami. W tej kolejce występuje rywalizacja, gdy wielu producentów próbujeoffer()
, ale z definicji jest to współbieżność. Jest to w zasadzie wydajna kolejka bez blokowania ogólnego przeznaczenia.A jeśli chodzi o to, że nie jest to
BlockingQueue
, no cóż, blokowanie wątku w celu oczekiwania w kolejce, to niesamowicie okropny sposób projektowania systemów współbieżnych. Nie. Jeśli nie możesz dowiedzieć się, jak użyćConcurrentLinkedQueue
scenariusza konsumenta / producenta, po prostu przełącz się na abstrakcje wyższego poziomu, takie jak dobre ramy dla aktorów.źródło
LinkedBlockingQueue
blokuje konsumenta lub producenta, gdy kolejka jest pusta lub pełna, a odpowiedni wątek konsumenta / producenta jest uśpiony. Ale ta funkcja blokowania wiąże się z kosztami: każda operacja typu put lub take jest blokowana między producentami lub konsumentami (jeśli jest ich wielu), więc w scenariuszach z wieloma producentami / konsumentami operacja może być wolniejsza.ConcurrentLinkedQueue
nie używa blokad, ale CAS w swoich operacjach typu put / take potencjalnie redukuje rywalizację z wieloma wątkami producentów i konsumentów. Ale bycie „wolne” czekać strukturę danych,ConcurrentLinkedQueue
nie będzie blokować gdy pusty, co oznacza, że konsument będzie musiał uporać się ztake()
powracającymnull
wartości przez „zajęty oczekiwania”, na przykład, z gwintem konsument pochłania CPU.Zatem, który z nich jest „lepszy”, zależy od liczby wątków konsumenckich, tempa ich konsumpcji / produkcji itp. Dla każdego scenariusza potrzebny jest punkt odniesienia.
Jeden szczególny przypadek użycia, w którym
ConcurrentLinkedQueue
jest wyraźnie lepiej, to sytuacja, gdy producenci najpierw coś wytwarzają i kończą pracę, umieszczając pracę w kolejce i dopiero po tym, jak konsumenci zaczną konsumować, wiedząc, że zrobią to, gdy kolejka będzie pusta. (tutaj nie ma współbieżności między producentem-konsumentem, ale tylko między producentem-producentem a konsumentem-konsumentem)źródło
unbounded blockingqueue
zostanie użyte, byłoby lepsze niż równoległe oparte na CASConcurrentLinkedQueue
Innym rozwiązaniem (które nie skaluje się dobrze) są kanały rendezvous: java.util.concurrent SynchronousQueue
źródło
Jeśli kolejka nie jest rozwijalna i zawiera tylko jeden wątek producenta / konsumenta. Możesz korzystać z kolejki bez blokady (nie musisz blokować dostępu do danych).
źródło