Что такое пул потоков?
Пул потоков (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
- Если активных потоков <
corePoolSize– создаётся новый поток для задачи. - Если потоков >=
corePoolSize– задача помещается в очередь. - Если очередь заполнена и потоков <
maximumPoolSize– создаётся новый поток. - Если очередь заполнена и потоков >=
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 задач.