Открыть сервис

LinkedBlockingQueue

LinkedBlockingQueue — это класс из пакета java.util.concurrent языка программирования Java, реализующий потокобезопасную блокирующую очередь с опционально ограниченной ёмкостью на основе связного списка. Относится к категории конкурентных коллекций, предназначенных для использования в многопоточных приложениях.

Основные характеристики

LinkedBlockingQueue реализует интерфейсы BlockingQueue, Queue и Collection. Её внутренняя структура построена на узлах связанного списка, каждый из которых содержит элемент данных и ссылку на следующий узел. Очередь поддерживает принцип FIFO (first-in-first-out — «первым вошёл — первым вышел»): элементы извлекаются в том же порядке, в котором были добавлены.

Ключевой особенностью является возможность задания максимальной ёмкости очереди при создании. Если ёмкость не указана, она по умолчанию равна Integer.MAX_VALUE, что делает очередь фактически неограниченной. При достижении предела ёмкости операции добавления блокируются до освобождения места.

Потокобезопасность

Внутренняя синхронизация реализована с помощью двух отдельных блокировок (ReentrantLock): одна для операций постановки (put) и другая для операций взятия (take). Это позволяет одновременно выполнять добавление и извлечение элементов разными потоками без взаимной блокировки, что повышает производительность по сравнению с очередями, использующими единую блокировку (например, ArrayBlockingQueue).

Конструкторы

Класс предоставляет три конструктора:

  • LinkedBlockingQueue() — создаёт очередь с ёмкостью Integer.MAX_VALUE.
  • LinkedBlockingQueue(int capacity) — создаёт очередь с заданной максимальной ёмкостью. Если capacity меньше или равно 0, выбрасывается исключение IllegalArgumentException.
  • LinkedBlockingQueue(Collection<? extends E> c) — создаёт очередь, содержащую элементы указанной коллекции. Ёмкость устанавливается равной Integer.MAX_VALUE. Если коллекция содержит null, выбрасывается NullPointerException.

Методы

LinkedBlockingQueue поддерживает все методы интерфейса BlockingQueue, которые делятся на четыре группы в зависимости от поведения при нарушении условий (очередь пуста или переполнена):

Группа методовДобавлениеИзвлечениеПросмотр (без удаления)
Исключениеadd(e) — выбрасывает IllegalStateExceptionremove() — выбрасывает NoSuchElementExceptionelement() — выбрасывает NoSuchElementException
Специальное значениеoffer(e) — возвращает falsepoll() — возвращает nullpeek() — возвращает null
Блокировкаput(e) — ждёт, пока освободится местоtake() — ждёт, пока появится элемент
Таймаутoffer(e, time, unit) — возвращает false по истечении таймаутаpoll(time, unit) — возвращает null по истечении таймаута

Дополнительно поддерживаются методы drainTo(Collection) и drainTo(Collection, int), которые атомарно перемещают заданное количество элементов из очереди в указанную коллекцию.

Особенности обработки null

LinkedBlockingQueue не допускает вставку элементов null. При попытке добавить null выбрасывается NullPointerException. Это сделано для того, чтобы методы poll() и peek() могли использовать null как индикатор отсутствия элемента.

Применение

LinkedBlockingQueue широко применяется в многопоточных приложениях Java для организации взаимодействия между потоками-производителями (producers) и потоками-потребителями (consumers). Типичные сценарии использования:

  • Пул потоков (Thread Pool Executor) — класс ThreadPoolExecutor использует BlockingQueue для хранения задач, ожидающих выполнения. LinkedBlockingQueue является одной из стандартных реализаций, применяемых по умолчанию.
  • Архитектура «производитель-потребитель» — один или несколько потоков генерируют данные и помещают их в очередь, а другие потоки извлекают и обрабатывают их.
  • Асинхронная обработка запросов — в веб-серверах и сервисах для буферизации входящих запросов перед их обработкой.
  • Потокобезопасный обмен данными между компонентами — в приложениях с графическим интерфейсом (Swing, JavaFX) для передачи событий между рабочими потоками и потоком обработки событий.

Сравнение с ArrayBlockingQueue

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

ХарактеристикаLinkedBlockingQueueArrayBlockingQueue
Внутренняя структураСвязный списокМассив фиксированного размера
ЁмкостьОпционально ограничена (по умолчанию неограничена)Фиксирована, задаётся при создании
БлокировкиДве отдельные блокировки (put/take)Одна общая блокировка
ПроизводительностьВыше при большом количестве потоковНиже при высокой конкуренции
Потребление памятиВыше (узлы списка содержат ссылки)Ниже (массив фиксированного размера)
Гарантия порядкаFIFOFIFO (опционально с fairness)

Производительность

LinkedBlockingQueue демонстрирует высокую пропускную способность в сценариях с интенсивной конкуренцией за счёт раздельных блокировок. Однако при очень малом размере очереди (менее 10 элементов) или при почти полном заполнении/опустошении очереди накладные расходы на управление списком могут быть выше, чем у ArrayBlockingQueue.

В тестах производительности для типичных сценариев «производитель-потребитель» LinkedBlockingQueue часто показывает лучшие результаты при количестве потоков более 4–8, особенно при неограниченной ёмкости.

История

Класс LinkedBlockingQueue был введён в Java 5 (версия 1.5) в составе пакета java.util.concurrent, разработанного под руководством Дугласа Ли (Doug Lea). Этот пакет стал частью стандартной библиотеки Java и включил в себя набор потокобезопасных коллекций и инструментов для многопоточного программирования.

В последующих версиях Java класс не претерпел значительных изменений, оставаясь стабильным и широко используемым компонентом.

Пример использования

```java import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.BlockingQueue;

public class ProducerConsumerExample { public static void main(String[] args) { BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);

Thread producer = new Thread(() -> { try { for (int i = 0; i < 100; i++) { queue.put(i); System.out.println("Produced: " + i); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } });

Thread consumer = new Thread(() -> { try { for (int i = 0; i < 100; i++) { Integer value = queue.take(); System.out.println("Consumed: " + value); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } });

producer.start(); consumer.start(); } } ```

Источники

  • Java Documentation: Class LinkedBlockingQueue (Oracle)
  • Doug Lea, «Concurrent Programming in Java: Design Principles and Patterns», 2nd Edition
  • Brian Goetz et al., «Java Concurrency in Practice», Addison-Wesley, 2006
  • OpenJDK source code: java.util.concurrent.LinkedBlockingQueue

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →