С какими реализациями интерфейса Queue вы работали в Java?

«С какими реализациями интерфейса Queue вы работали в Java?» — вопрос из категории Брокеры сообщений, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Java работал со следующими реализациями и типами очередей:

Базовые очереди (java.util.Queue)

  • LinkedList — простая реализация FIFO-очереди на основе двусвязного списка. Не потокобезопасна.
    Queue<String> queue = new LinkedList<>();
    queue.offer("First");
    queue.offer("Second");
    String first = queue.poll(); // "First"
  • PriorityQueue — очередь, где элементы извлекаются согласно их приоритету (заданному Comparator или натуральному порядку), а не по принципу FIFO. Не потокобезопасна.
    Queue<Integer> pq = new PriorityQueue<>();
    pq.add(5);
    pq.add(1);
    pq.add(3);
    System.out.println(pq.poll()); // 1 (наименьший элемент)
  • ArrayDeque — эффективная двусторонняя очередь на основе массива. Часто используется как стек (push/pop) или очередь.

Потокобезопасные (блокирующие) очереди (java.util.concurrent)

  • ArrayBlockingQueue — очередь фиксированной емкости на основе массива. Блокирует поток при попытке добавить элемент в полную очередь или извлечь из пустой.
  • LinkedBlockingQueue — очередь на основе связного списка. Может быть ограниченной или практически неограниченной. Часто используется в пулах потоков (например, в ThreadPoolExecutor).
  • PriorityBlockingQueue — потокобезопасный аналог PriorityQueue.
  • SynchronousQueue — очередь с нулевой емкостью. Каждая операция вставки (put) ждет соответствующей операции извлечения (take) другим потоком, и наоборот. Используется для прямой передачи задач между потоками.

Неблокирующие потокобезопасные очереди

  • ConcurrentLinkedQueue — неограниченная потокобезопасная очередь на основе связного списка, использующая неблокирующие алгоритмы (CAS). Обеспечивает высокую производительность в условиях высокой конкуренции.

Выбор реализации:

  • LinkedBlockingQueue — для классических producer-consumer сценариев с балансом производительности и простоты.
  • ArrayBlockingQueue — когда нужна фиксированная емкость и предсказуемое потребление памяти.
  • ConcurrentLinkedQueue — для неблокирующих операций и высокой конкурентности.
  • PriorityBlockingQueue — когда порядок обработки элементов определяется приоритетом.