Что такое Queue и какие реализации существуют?
4. ConcurrentLinkedQueue — lock-free 5. BlockingQueue — Producer-Consumer 6. null запрещён в большинстве очередей 7. SynchronousQueue — для прямой передачи
🟢 Junior Level
Queue (Очередь) — структура данных по принципу FIFO (First In, First Out).
Простая аналогия: Очередь в магазине. Кто первый пришёл, тот первый обслужен.
Основные методы:
| Действие | Исключение | Безопасный метод |
|---|---|---|
| Добавить | add(e) |
offer(e) |
| Забрать | remove() |
poll() |
| Посмотреть | element() |
peek() |
Queue<String> queue = new ArrayDeque<>();
queue.offer("A"); // Добавили
queue.offer("B");
String first = queue.poll(); // → "A" (первый ушёл)
String next = queue.peek(); // → "B" (посмотрели)
offer/poll/peek предпочтительнее в production-коде, потому что они не бросают исключения — возвращают false/null при ошибке, что позволяет обработать ситуацию gracefully.
🟡 Middle Level
Основные реализации
ArrayDeque:
// Кольцевой массив → O(1) вставка/удаление
Queue<String> queue = new ArrayDeque<>();
queue.offer("A"); // Быстро
queue.poll(); // Быстро
PriorityQueue:
// Binary Heap → извлечение по приоритету
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(5);
pq.offer(1);
pq.offer(3);
pq.poll(); // → 1 (наименьший!)
Методы: Exception vs Special Value
// Бросают exception:
queue.add(e); // IllegalStateException если full
queue.remove(); // NoSuchElementException если пуста
queue.element(); // NoSuchElementException если пуста
// Возвращают special value:
queue.offer(e); // false если full
queue.poll(); // null если пуста
queue.peek(); // null если пуста
// ✅ В production используйте offer/poll/peek!
Thread-safe очереди
// Lock-free (высокая интенсивность)
Queue<String> q = new ConcurrentLinkedQueue<>();
// Blocking (Producer-Consumer)
BlockingQueue<String> q = new LinkedBlockingQueue<>();
q.put("A"); // Блокирует если full
String e = q.take(); // Блокирует если empty
// Bounded (контроль памяти)
BlockingQueue<String> q = new ArrayBlockingQueue<>(100);
// → Не больше 100 элементов → защита от OOM
🔴 Senior Level
Когда НЕ использовать Queue
- Если нужен доступ к произвольному элементу — возьмите List
- Если важна уникальность — возьмите Set
- Если данные нужно сортировать — PriorityQueue с компаратором или TreeSet
Почему очереди не любят null?
// Большинство очередей запрещают null
queue.offer(null); // → NullPointerException!
// Почему?
// poll() и peek() возвращают null для пустой очереди
// Если null валидный элемент → двусмысленность!
// → "очередь пуста" или "в голове null"?
BlockingQueue паттерны
// Producer-Consumer
BlockingQueue<Task> queue = new LinkedBlockingQueue<>(1000);
// Producer
queue.put(task); // Блокирует если full → backpressure!
**Backpressure** (обратное давление) — механизм, при котором медленный потребитель сигнализирует быстрому производителю замедлиться. Bounded Queue блокирует producer, пока consumer не освободит место.
// Consumer
Task task = queue.take(); // Блокирует если empty
SynchronousQueue
// Очередь с нулевой ёмкостью!
BlockingQueue<String> q = new SynchronousQueue<>();
// put блокирует, пока кто-то не сделает take
// Блокировка = поток «засыпает» и не расходует CPU, пока другой поток не заберёт элемент. Это эффективно для синхронизации без активного ожидания (busy-wait).
q.put("A"); // Ждёт...
// В другом потоке:
String s = q.take(); // → "A", put разблокируется
// Использование: Executors.newCachedThreadPool()
DelayQueue
// Элементы доступны только после задержки
class DelayedTask implements Delayed {
private final long deadline;
public long getDelay(TimeUnit unit) {
return unit.convert(deadline - System.currentTimeMillis(), MILLISECONDS);
}
}
DelayQueue<DelayedTask> queue = new DelayQueue<>();
// poll() вернёт элемент только когда getDelay() <= 0
TransferQueue (Java 7+)
// Producer ждёт, пока Consumer заберёт элемент
TransferQueue<String> q = new LinkedTransferQueue<>();
// Producer
q.transfer("message"); // Блокирует пока consumer не заберёт!
// Consumer
String msg = q.take(); // Забирает, producer разблокируется
// Использование: синхронная передача данных
// Синхронная передача = producer ждёт конкретного момента, когда consumer *примет* элемент (а не просто положит в буфер). Аналог: передача файла из рук в руки, а не оставление в почтовом ящике.
Production Experience
Реальный сценарий: Bounded Queue спасла от OOM
- Producer быстрее Consumer → очередь росла
- Unbounded Queue → OOM через 2 часа
- Решение: ArrayBlockingQueue(10000)
- Результат: Producer блокируется → стабильная система
Best Practices
- offer/poll/peek вместо add/remove/element
- ArrayDeque — для single-thread
- Bounded Queue — для backpressure
- ConcurrentLinkedQueue — lock-free
- BlockingQueue — Producer-Consumer
- null запрещён в большинстве очередей
- SynchronousQueue — для прямой передачи
Резюме для Senior
- Queue = FIFO, offer/poll/peek предпочтительнее
- ArrayDeque — fastest single-thread
- BlockingQueue — Producer-Consumer с backpressure
- Bounded — защита от OOM
- ConcurrentLinkedQueue — lock-free
- TransferQueue — синхронная передача
- null запрещён (двусмысленность с poll)
🎯 Шпаргалка для интервью
Обязательно знать:
- Queue = FIFO (First In, First Out), методы: offer/poll/peek предпочтительнее add/remove/element
- offer/poll/peek возвращают false/null вместо exception — безопаснее для production
- ArrayDeque — fastest single-thread (кольцевой массив, O(1) вставка/удаление)
- PriorityQueue = Binary Heap, извлечение по приоритету (наименьший элемент), O(log n)
- BlockingQueue = Producer-Consumer паттерн, put/take блокируют, bounded queue = backpressure
- SynchronousQueue = нулевая ёмкость, producer ждёт пока consumer заберёт элемент
- TransferQueue (Java 7+) — синхронная передача: transfer() блокирует пока consumer не заберёт
- null запрещён в большинстве очередей — двусмысленность с poll() возвращающим null
Частые уточняющие вопросы:
- Почему offer/poll/peek лучше add/remove/element? — Не бросают exception, позволяют обработать gracefully
- Зачем bounded queue? — Backpressure: медленный потребитель блокирует быстрого производителя, защита от OOM
- Чем SynchronousQueue отличается от обычной очереди? — Нулевая ёмкость, put блокирует пока кто-то не сделает take
- Когда использовать TransferQueue? — Для синхронной передачи данных, producer ждёт конкретного момента приёма
Красные флаги (НЕ говорить):
- ❌ “Queue позволяет хранить null” — большинство реализаций запрещают null (двусмысленность с poll)
- ❌ “Unbounded Queue — нормальный выбор для production” — без bounded queue риск OOM от быстрого производителя
- ❌ “add/remove/element — лучший выбор” — они бросают exception, offer/poll/peek безопаснее
- ❌ “PriorityQueue извлекает наибольший элемент” — по умолчанию извлекает НАИМЕНЬШИЙ (нужен обратный Comparator для наибольшего)
Связанные темы:
- [[Какие основные интерфейсы Collection Framework]]
- [[Что такое Deque]]
- [[В чём разница между List, Set и Queue]]
- [[Что такое Stack]]