Создание потока — системный вызов, выделение стека (около мегабайта), регистрация в планировщике. Делать это на каждый запрос дорого, а главное — неограниченно: при всплеске нагрузки тысячи потоков съедят память и время на переключения. Пул решает обе проблемы: фиксированный набор рабочих потоков берёт задачи из очереди.
**Как устроен ThreadPoolExecutor** в Java:
- пока потоков меньше
corePoolSize, на новую задачу создаётся поток; - дальше задачи кладутся в очередь;
- если очередь полна, создаются потоки до
maximumPoolSize; - если и это исчерпано — срабатывает политика отказа (
RejectedExecutionHandler): исключение, выполнение в вызывающем потоке (CallerRunsPolicy — естественный backpressure), выбросить задачу.
Ловушка: Executors.newFixedThreadPool использует неограниченную очередь. При перегрузке задачи копятся, память растёт, задержка каждой задачи — время в очереди, и в итоге OOM. Неограниченный newCachedThreadPool создаёт потоки без предела.
Размер пула:
- CPU-bound — около числа ядер (иногда +1): больше потоков не ускорят, только добавят переключений;
- I/O-bound — формула из «Java Concurrency in Practice»: N = ядра × загрузка × (1 + W/C), где W — время ожидания, C — время вычислений. Задача ждёт базу 90 мс и считает 10 мс на 8 ядрах — 8 × (1 + 9) = 80 потоков;
- но верхний предел часто задаёт не формула, а зависимость: нет смысла в 200 потоках, если пул соединений к базе — 20. Это закон Литтла: параллелизм = пропускная способность × время ответа.
Изоляция (bulkhead). Разные зависимости — разные пулы: если медленный внешний сервис займёт все потоки общего пула, встанут и запросы, которые с ним не связаны.
Другие подходы: виртуальные потоки Java 21 — дешёвые, можно на задачу, но ограничивать параллелизм к ресурсам всё равно нужно (семафором); ForkJoinPool — для рекурсивных вычислительных задач с work stealing; event loop — для большого числа соединений.
Не забыть: корректно останавливать пул (shutdown + awaitTermination), давать потокам имена, обрабатывать исключения в задачах — в submit они прячутся внутри Future.