LinkedBlockingQueue срещу ConcurrentLinkedQueue

1. Въведение

LinkedBlockingQueue и ConcurrentLinkedQueue са двете най-често използвани едновременни опашки в Java . Въпреки че и двете опашки често се използват като едновременна структура от данни, между тях има фини характеристики и поведенчески разлики.

В този кратък урок ще обсъдим и двете опашки и ще обясним техните прилики и разлики.

2. LinkedBlockingQueue

В LinkedBlockingQueue е по избор-ограничена блокира изпълнението на опашката, което означава, че размерът на опашката може да бъде определен, ако е необходимо.

Нека създадем LinkedBlockingQueue, която може да съдържа до 100 елемента:

BlockingQueue boundedQueue = new LinkedBlockingQueue(100);

Също така можем да създадем неограничена LinkedBlockingQueue, просто като не посочим размера:

BlockingQueue unboundedQueue = new LinkedBlockingQueue();

Неограничена опашка предполага, че размерът на опашката не е посочен по време на създаването. Следователно опашката може да нараства динамично, когато към нея се добавят елементи. Ако обаче не е останала памет, опашката изхвърля java.lang.OutOfMemoryError.

Можем да създадем LinkedBlockingQueue и от съществуваща колекция:

Collection listOfNumbers = Arrays.asList(1,2,3,4,5); BlockingQueue queue = new LinkedBlockingQueue(listOfNumbers);

На LinkedBlockingQueue клас изпълнява BlockingQueue интерфейс, който осигурява блокиране на природата, за да го .

Блокиращата опашка показва, че опашката блокира нишката за достъп, ако е пълна (когато опашката е ограничена) или стане празна. Ако опашката е пълна, добавянето на нов елемент ще блокира нишката за достъп, освен ако няма място за новия елемент. По същия начин, ако опашката е празна, тогава достъпът до елемент блокира извикващата нишка:

ExecutorService executorService = Executors.newFixedThreadPool(1); LinkedBlockingQueue queue = new LinkedBlockingQueue(); executorService.submit(() -> { try { queue.take(); } catch (InterruptedException e) { // exception handling } });

В горния кодов фрагмент имаме достъп до празна опашка. Следователно методът take блокира извикващата нишка.

Функцията за блокиране на LinkedBlockingQueue е свързана с известни разходи. Тази цена се дължи на факта, че всяка операция за пускане или вземане се заключва между производителя или потребителските нишки. Следователно, при сценарии с много производители и потребители, пускането и предприемането на действия може да бъде по-бавно.

3. ConcurrentLinkedQueue

А ConcurrentLinkedQueue е неограничена, конци-безопасно и без блокиране на опашката.

Нека създадем празна ConcurrentLinkedQueue :

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

Можем да създадем ConcurrentLinkedQueue и от съществуваща колекция:

Collection listOfNumbers = Arrays.asList(1,2,3,4,5); ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(listOfNumbers);

За разлика от LinkedBlockingQueue, а ConcurrentLinkedQueue е без блокиране на опашка . По този начин той не блокира нишка, след като опашката е празна. Вместо това връща null . Тъй като е неограничен, той ще хвърли java.lang.OutOfMemoryError, ако няма допълнителна памет за добавяне на нови елементи.

Освен че не блокира, ConcurrentLinkedQueue има и допълнителна функционалност.

Във всеки сценарий производител-потребител потребителите няма да се задоволят с производителите; много производители обаче ще се борят помежду си:

int element = 1; ExecutorService executorService = Executors.newFixedThreadPool(2); ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); Runnable offerTask = () -> queue.offer(element); Callable pollTask = () -> { while (queue.peek() != null) { return queue.poll().intValue(); } return null; }; executorService.submit(offerTask); Future returnedElement = executorService.submit(pollTask); assertThat(returnedElement.get().intValue(), is(equalTo(element))); 

Първата задача, offerTask , добавя елемент към опашката, а втората задача, pollTask, извлича елемент от опашката. Задачата за анкета допълнително проверява първо опашката за елемент, тъй като ConcurrentLinkedQueue не е блокираща и може да върне нулева стойност .

4. Прилики

Както LinkedBlockingQueue, така и ConcurrentLinkedQueue са изпълнения на опашката и споделят някои общи характеристики. Нека обсъдим приликите на тези две опашки:

  1. И двете изпълняват интерфейса на опашката
  2. И двамата използват свързани възли, за да съхраняват своите елементи
  3. И двете са подходящи за едновременни сценарии за достъп

5. Различия

Въпреки че и двете опашки имат известни прилики, има и съществени разлики в характеристиките:

Особеност LinkedBlockingQueue ConcurrentLinkedQueue
Блокиране на природата Това е блокираща опашка и изпълнява интерфейса BlockingQueue Това е неблокираща опашка и не прилага интерфейса BlockingQueue
Размер на опашката Това е по избор ограничена опашка, което означава, че има разпоредби за определяне на размера на опашката по време на създаването Това е неограничена опашка и няма разпоредба за определяне на размера на опашката по време на създаването
Заключване на природата Това е опашка, базирана на заключване Това е опашка без заключване
Алгоритъм Той реализира заключването си на базата на алгоритъм за двустранна опашка Разчита на алгоритъма на Michael & Scott за неблокиращи опашки без заключване
Изпълнение В алгоритъма на алгоритъма за опашка с две заключения LinkedBlockingQueue използва две различни ключалки - putLock и takeLock . Операциите за пускане / вземане използват първия тип заключване, а операциите за вземане / анкетиране използват другия тип заключване Той използва CAS (Сравнение и размяна ) за своите операции
Блокиращо поведение Това е блокираща опашка. И така, той блокира нишките за достъп, когато опашката е празна Той не блокира нишката за достъп, когато опашката е празна и връща null

6. Заключение

В тази статия научихме за LinkedBlockingQueue и ConcurrentLinkedQueue.

Първо, обсъдихме индивидуално тези две реализации на опашката и някои от техните характеристики. След това видяхме приликите между тези две реализации на опашката. Накрая проучихме разликите между тези две реализации на опашката.

Както винаги, изходният код на примерите е достъпен в GitHub.