Gymterview
middle

Что такое пул потоков?

Пул потоков (thread pool) – это механизм переиспользования заранее созданных потоков для выполнения множества задач. Вместо создания нового потока для каждой задачи, задачи помещаются в очередь, а потоки из пула по очереди их выполняют.

Зачем нужен пул потоков

Проблема без пула Решение с пулом
Создание потока – дорогая операция (выделение стека ~512 КБ-1 МБ, системный вызов) Потоки создаются один раз и переиспользуются
Неограниченное создание потоков может привести к OutOfMemoryError Фиксированное или контролируемое число потоков
Сложно управлять жизненным циклом потоков ExecutorService предоставляет shutdown(), awaitTermination()

Архитектура Executor Framework (Java 5+)

Пример
Executor                    ← execute(Runnable)
  └── ExecutorService       ← submit(), shutdown(), invokeAll()
        ├── AbstractExecutorService
        │     └── ThreadPoolExecutor     ← Основная реализация
        └── ForkJoinPool                 ← Work-stealing пул

Типы пулов (фабричные методы Executors)

Метод Размер пула Очередь Применение
newFixedThreadPool(N) Фиксированный: N потоков LinkedBlockingQueue (неограниченная) CPU-bound задачи, стабильная нагрузка
newCachedThreadPool() 0…Integer.MAX_VALUE SynchronousQueue (без хранения) Много коротких IO-bound задач
newSingleThreadExecutor() 1 поток LinkedBlockingQueue Последовательное выполнение задач
newScheduledThreadPool(N) N core-потоков DelayedWorkQueue Периодические и отложенные задачи
newWorkStealingPool() (Java 8) По числу ядер Очереди work-stealing Рекурсивные задачи, параллельные вычисления
newVirtualThreadPerTaskExecutor() (Java 21) Без ограничений (виртуальные) Нет очереди IO-bound задачи

Параметры ThreadPoolExecutor

ThreadPoolExecutor – самая гибкая реализация, принимает 7 параметров:

Пример
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,    // Минимум потоков (сохраняются даже при простое)
    maximumPoolSize, // Максимум потоков
    keepAliveTime,   // Время жизни «лишних» потоков
    TimeUnit.SECONDS,
    workQueue,       // Очередь задач
    threadFactory,   // Фабрика для создания потоков
    rejectionHandler // Обработчик отклонённых задач
);

Логика работы ThreadPoolExecutor

  1. Если активных потоков < corePoolSize – создаётся новый поток для задачи.
  2. Если потоков >= corePoolSize – задача помещается в очередь.
  3. Если очередь заполнена и потоков < maximumPoolSize – создаётся новый поток.
  4. Если очередь заполнена и потоков >= maximumPoolSize – вызывается RejectedExecutionHandler.

Virtual Threads (Java 21+)

Пример: Executor с виртуальными потоками
// Java 21+ — Executor с виртуальными потоками
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return callExternalApi();
        });
    }
} // close() ждёт завершения всех задач

Рекомендации по выбору пула (2026)

Тип задач Рекомендуемый пул
IO-bound (HTTP, БД, файлы) newVirtualThreadPerTaskExecutor() (Java 21+) или newCachedThreadPool()
CPU-bound (вычисления) newFixedThreadPool(N), N ~ количеству ядер, или ForkJoinPool
Периодические задачи newScheduledThreadPool()
Единственная фоновая задача newSingleThreadExecutor()

Аналогия из жизни. Пул потоков – это бригада рабочих на стройке. Вместо того чтобы нанимать нового рабочего для каждой задачи (долго и дорого), бригадир (пул) раздаёт задачи из очереди свободным рабочим. Когда рабочий заканчивает одну задачу, он берёт следующую.

На собеседовании. Покажите знание: (1) зачем нужен пул потоков (экономия ресурсов, контроль параллелизма); (2) основные типы пулов и когда какой использовать; (3) параметры ThreadPoolExecutor и логику его работы (core → queue → max → reject); (4) для Java 21+ упомяните виртуальные потоки как замену CachedThreadPool для IO-bound задач.