Короткий ответ
Пул потоков — это набор заранее созданных рабочих потоков, которые переиспользуются для выполнения множества задач. Вместо создания нового потока на каждую задачу вы отправляете задачу в пул, её выполняет свободный поток, после чего поток не умирает, а возвращается в пул и ждёт следующую задачу. Пул убирает накладные расходы на создание и уничтожение потоков и ограничивает максимальное число одновременно работающих потоков.
Как это работает подробнее
В Java пулы реализованы через интерфейс ExecutorService и его основную реализацию ThreadPoolExecutor. Фабричные методы класса Executors: newFixedThreadPool(n) — фиксированное число потоков; newCachedThreadPool() — потоки по мере необходимости с переиспользованием простаивающих; newSingleThreadExecutor() — один рабочий поток с неограниченной очередью; newScheduledThreadPool() — для отложенных и периодических задач.
Под капотом ThreadPoolExecutor настраивается параметрами: corePoolSize — основные потоки, maximumPoolSize — максимум, keepAliveTime — время удержания лишних потоков, и очередью задач — обычно BlockingQueue (LinkedBlockingQueue, SynchronousQueue).
- Если все потоки заняты, задача встаёт в очередь; когда очередь заполнена и достигнут максимум потоков, срабатывает
RejectedExecutionHandler— по умолчанию (AbortPolicy) выбрасываетсяRejectedExecutionException. submit()в отличие отexecute()принимает иCallable, возвращаяFuture; корректное завершение —shutdown()и ожидание черезawaitTermination().
Пример кода
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> System.out.println("Задача в пуле"));
executor.shutdown(); // больше задач не принимаем, ждём завершения текущих
}
}Что использовать на практике
- Используй пул для массовых однородных задач: это быстрее и дешевле, чем поток на каждую задачу.
- Подбирай размер пула под число ядер и характер задач, а не «на глаз».
- Завершай пул через
shutdown()/awaitTermination(), чтобы приложение корректно остановилось.
Подводные камни
- У
newFixedThreadPoolочередь неограниченна: при переполнении задачи копятся и растёт расход памяти. newCachedThreadPoolпри шквале задач создаёт потоки без верхней границы.shutdownNow()прерывает работающие потоки — задачи могут остаться невыполненными.
Как отвечать на собеседовании
- Определение: пул — переиспользуемые рабочие потоки → две проблемы, которые он решает (расходы на создание потоков и ограничение их числа) → покажи ExecutorService и фабрики Executors → загляни под капот: ThreadPoolExecutor и его параметры → поведение при переполнении и правильное завершение пула.