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) — выбрасывает IllegalStateException | remove() — выбрасывает NoSuchElementException | element() — выбрасывает NoSuchElementException |
| Специальное значение | offer(e) — возвращает false | poll() — возвращает null | peek() — возвращает 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. Основные различия:
| Характеристика | LinkedBlockingQueue | ArrayBlockingQueue |
|---|---|---|
| Внутренняя структура | Связный список | Массив фиксированного размера |
| Ёмкость | Опционально ограничена (по умолчанию неограничена) | Фиксирована, задаётся при создании |
| Блокировки | Две отдельные блокировки (put/take) | Одна общая блокировка |
| Производительность | Выше при большом количестве потоков | Ниже при высокой конкуренции |
| Потребление памяти | Выше (узлы списка содержат ссылки) | Ниже (массив фиксированного размера) |
| Гарантия порядка | FIFO | FIFO (опционально с 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 →