Очереди задач в Java: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Очереди задач в Java: от основ до многопоточных решений

Очередь задач (Task Queue) — один из фундаментальных паттернов в разработке программного обеспечения. В Java этот механизм реализован через интерфейс Queue и его многочисленные реализации. Очередь позволяет организовать обработку задач в порядке их поступления (FIFO — First In, First Out), что особенно важно при работе с пулами потоков, асинхронными операциями и распределёнными системами.

В этой статье мы подробно разберём, как работать с очередями задач в Java: от базового интерфейса до продвинутых многопоточных реализаций. Вы узнаете, какие классы использовать в разных сценариях и как избежать типичных ошибок.

1. Интерфейс Queue и базовые операции

Интерфейс Queue является частью Java Collections Framework. Он определяет основные методы для работы с очередью:

  • add(E e) — добавляет элемент, выбрасывает исключение при переполнении;
  • offer(E e) — добавляет элемент, возвращает false при неудаче;
  • remove() — удаляет и возвращает головной элемент, выбрасывает исключение, если очередь пуста;
  • poll() — удаляет и возвращает головной элемент, возвращает null при пустой очереди;
  • element() — возвращает головной элемент без удаления, исключение при пустой очереди;
  • peek() — возвращает головной элемент без удаления, null при пустой очереди.

Пример базовой работы с очередью:

import java.util.LinkedList;
import java.util.Queue;

public class SimpleQueueExample {
    public static void main(String[] args) {
        Queue<String> taskQueue = new LinkedList<>();

        // Добавление задач
        taskQueue.offer("Задача 1: Обработка заказа");
        taskQueue.offer("Задача 2: Отправка email");
        taskQueue.offer("Задача 3: Генерация отчета");

        System.out.println("Текущая задача: " + taskQueue.peek());
        System.out.println("Размер очереди: " + taskQueue.size());

        // Обработка всех задач
        while (!taskQueue.isEmpty()) {
            String task = taskQueue.poll();
            System.out.println("Выполняется: " + task);
        }
    }
}

Результат выполнения:

Текущая задача: Задача 1: Обработка заказа
Размер очереди: 3
Выполняется: Задача 1: Обработка заказа
Выполняется: Задача 2: Отправка email
Выполняется: Задача 3: Генерация отчета

2. Основные реализации очередей

Java предлагает несколько реализаций интерфейса Queue, каждая из которых подходит для определённых сценариев.

2.1. LinkedList как очередь

LinkedList — одна из самых простых и универсальных реализаций. Она работает как двунаправленный список, что позволяет быстро добавлять и удалять элементы с обоих концов. Однако для больших объёмов данных производительность может снижаться из-за накладных расходов на хранение ссылок.

Queue<Integer> linkedQueue = new LinkedList<>();
linkedQueue.add(10);
linkedQueue.add(20);
linkedQueue.add(30);
System.out.println(linkedQueue.poll()); // 10

2.2. PriorityQueue — очередь с приоритетами

PriorityQueue не соблюдает строгий порядок FIFO. Элементы извлекаются в порядке их приоритета, который задаётся либо естественным порядком (Comparable), либо компаратором. Это идеальный выбор для систем, где одни задачи важнее других.

import java.util.PriorityQueue;
import java.util.Queue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        Queue<Task> priorityQueue = new PriorityQueue<>(
            (t1, t2) -> Integer.compare(t1.priority, t2.priority)
        );

        priorityQueue.offer(new Task("Низкий приоритет", 3));
        priorityQueue.offer(new Task("Высокий приоритет", 1));
        priorityQueue.offer(new Task("Средний приоритет", 2));

        while (!priorityQueue.isEmpty()) {
            System.out.println(priorityQueue.poll().name);
        }
    }

    static class Task {
        String name;
        int priority;
        Task(String name, int priority) {
            this.name = name;
            this.priority = priority;
        }
    }
}

Вывод:

Высокий приоритет
Средний приоритет
Низкий приоритет

2.3. ArrayDeque — эффективная двусторонняя очередь

ArrayDeque реализован на основе массива и обеспечивает отличную производительность как для работы с головой, так и с хвостом очереди. Он быстрее LinkedList и не требует синхронизации, что делает его хорошим выбором для однопоточных приложений.

import java.util.ArrayDeque;
import java.util.Deque;

public class ArrayDequeExample {
    public static void main(String[] args) {
        Deque<String> deque = new ArrayDeque<>();
        deque.addLast("Задача A");
        deque.addLast("Задача B");
        deque.addFirst("Срочная задача");

        System.out.println(deque.removeFirst()); // Срочная задача
        System.out.println(deque.removeLast());  // Задача B
    }
}

3. Многопоточные очереди задач

В реальных проектах очереди задач часто используются в многопоточной среде. Java предоставляет потокобезопасные реализации в пакете java.util.concurrent.

3.1. ConcurrentLinkedQueue — неблокирующая очередь

ConcurrentLinkedQueue использует алгоритм Michael-Scott для lock-free операций. Она идеальна для high-load систем, где важна производительность, но не требуется блокировка при пустой очереди.

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ConcurrentQueueExample {
    public static void main(String[] args) {
        Queue<String> concurrentQueue = new ConcurrentLinkedQueue<>();

        // Поток-производитель
        Thread producer = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                concurrentQueue.offer("Задача " + i);
                System.out.println("Добавлена задача " + i);
            }
        });

        // Поток-потребитель
        Thread consumer = new Thread(() -> {
            while (true) {
                String task = concurrentQueue.poll();
                if (task != null) {
                    System.out.println("Обработана: " + task);
                }
                if (concurrentQueue.isEmpty()) break;
            }
        });

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

3.2. BlockingQueue — блок

Блоги

Рекомендаций по книгам