Разница между LinkedBlockingQueue и ConcurrentLinkedQueue

Spring Boot Java Spring Spring Cloud

1. Простое открытие

LinkedBlockingQueueиConcurrentLinkedQueueЭто наиболее часто используемая очередь в сценариях высокой параллелизма Java. Хотя эти две очереди часто используются в качестве структур данных для одновременных сценариев, между ними существуют тонкие характеристики и поведенческие различия. В этой статье я расскажу вам о сходствах и различиях между ними. Приглашаем всех оставить сообщение для обсуждения ~

2. LinkedBlockingQueue

во-первыхLinkedBlockingQueueЭто "необязательная и ограниченная" блокирующая реализация очереди, вы можете указать размер очереди по мере необходимости. Далее я создамLinkedBlockingQueue, который может содержать до 100 элементов:

BlockingQueue<Integer> boundedQueue = new LinkedBlockingQueue<>(100);

Конечно, мы также можем создать неограниченныйLinkedBlockingQueue:

BlockingQueue<Integer> unboundedQueue = new LinkedBlockingQueue<>();

Неограниченная очередь означает, что размер очереди не указан во время создания. Таким образом, очередь может динамически расти по мере добавления элементов. Однако, если памяти не осталось, очередь выброситjava.lang.OutOfMemoryОшибка.

Вот вопрос для всех, чтобы подумать: это хорошо или плохо создавать неограниченную очередь?

Мы также можем создать из существующей коллекцииLinkedBlockingQueue:

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

LinkedBlockingQueue ДостигнутоBlockingQueueинтерфейс, который придает ему блокирующие свойства.

Блокирующая очередь означает, что если поток доступа заполнен (когда очередь ограничена) или становится пустым, очередь блокирует этот поток. Если очередь заполнена, добавление нового элемента заблокирует поток доступа, если для нового элемента не будет свободного места. Точно так же доступ к элементу блокирует вызывающий поток, если очередь пуста:

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

在上面的代码片段中,我们正在访问一个空队列。 следовательно,take()Метод блокирует вызывающий поток.

LinkedBlockingQueueБлокирующий характер 's связан с некоторыми накладными расходами. Эта цена обусловлена ​​тем, что каждыйputилиtakeОперации блокируются между потоками-производителями или потоками-потребителями. Таким образом, в случае многих производителей и потребителей,putИ принять меры может быть медленнее.

3. ConcurrentLinkedQueue

Сначала объявить,ConcurrentLinkedQueue Бесстраничный, Безопасен в потоке и отсутствие блокировки очереди

создать пустойConcurrentLinkedQueue:

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

Как и выше, мы также можем создать из существующей коллекцииConcurrentLinkedQueue:

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

отличается отLinkedBlockingQueue,ConcurrentLinkedQueueявляется неблокирующей очередью. Таким образом, даже если очередь пуста, поток не будет заблокирован. Вместо этого он возвращаетнулевойНУЛЕВОЙ.虽然它是无界的,但如果没有额外的内存来添加新元素,它依旧会抛出java.lang.OutOfMemoryОшибка. Помимо неблокировки,ConcurrentLinkedQueueЕсть и другие особенности. В любом сценарии производитель-потребитель потребители не будут удовлетворены производителями, однако несколько производителей будут конкурировать друг с другом:

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

первая задачаofferTaskДобавить элемент в очередь, вторая задачаpollTaskПолучить элементы из очереди.pollTask Сначала проверьте элементы в очереди, потому чтоConcurrentLinkedQueueявляется неблокирующим и может возвращатьсяnullценность.

4. Попросите согласия

LinkedBlockingQueueиConcurrentLinkedQueueОба являются реализациями очередей и имеют некоторые общие характеристики. Давайте обсудим сходство этих двух очередей:

  1. ВсевыполнитьQueueинтерфейс
  2. они оба используютlinked nodesузел хранения
  3. Оба подходят для параллельного доступа

5. Отличия

Хотя обе когорты имеют определенное сходство, есть и некоторые существенные характерные различия:

характеристика LinkedBlockingQueue ConcurrentLinkedQueue
Обструктивный заблокировать очередь и реализоватьblocking queueинтерфейс неблокирующая очередь, не реализованаblocking queueинтерфейс
размер очереди необязательная ограниченная очередь, что означает, что размер очереди может быть определен во время создания Неограниченная очередь, и нет возможности указать размер очереди во время создания.
функция блокировки очередь на основе блокировки безблокировочная очередь
алгоритм Реализация блокировки основана на алгоритме «две очереди блокировки» зависит отАлгоритм Майкла и Скотта для создания неблокирующей очереди без блокировки
выполнить существуетдвойная очередь блокировкиВ механизме алгоритмаLinkedBlockingQueueиспользуя два разных замка,putLockиtakeLock.put/takeВ операции используется первый тип блокировки,take/pollВ операции используется другой тип блокировки Используйте CAS (Compare And Swap) работать
блокирующее поведение Когда очередь пуста, она блокирует поток доступа Возвращает null, когда очередь пуста, поток доступа не блокируется

6. Обобщайте, чтобы улучшить

Во-первых, мы обсудим две реализации очередей по отдельности и некоторые их особенности, сходства и различия между ними. Делает ли это сравнение вас более впечатленными двумя очередями?

Обратите внимание на публичный номер: Большой парень вне банкаСеребряный блог тысячи рек