Получить доступ

Пулы потоков и ExecutorService

Зачем пул, параметры ThreadPoolExecutor, ловушки фабрик Executors, выбор размера и корректное завершение.

Зачем пул

Создавать new Thread на каждую задачу опасно: потоки дороги, и при всплеске нагрузки их число (а с ним и память/переключения контекста) выходит из-под контроля. Пул переиспользует ограниченное число потоков и ставит избыточные задачи в очередь.

ExecutorService отделяет ЧТО делать (задача Runnable/Callable) от ТОГО, как и где это выполняется (политика пула). Это и абстракция, и предохранитель.

Параметры ThreadPoolExecutor

ThreadPoolExecutor конфигурируется: corePoolSize (постоянные потоки), maximumPoolSize (потолок), keepAliveTime (через сколько лишние сверх core простаивающие потоки гибнут), workQueue (очередь задач), ThreadFactory (имена потоков, daemon) и RejectedExecutionHandler (что делать при переполнении).

Логика приёма задачи: сначала заполняются core-потоки; когда они заняты — задачи копятся в очереди; только если очередь заполнена, создаются потоки до maximum; если и это исчерпано — срабатывает политика отказа (AbortPolicy по умолчанию бросает исключение, CallerRunsPolicy исполняет в вызывающем потоке, давая backpressure).

// Так пул конфигурируют в проде: границы явные, отказ осознанный
ExecutorService pool = new ThreadPoolExecutor(
    8, 16,                                  // core / maximum потоков
    60, TimeUnit.SECONDS,                   // сколько живёт лишний поток без работы
    new ArrayBlockingQueue<>(1000),         // ОГРАНИЧЕННАЯ очередь — иначе OOM вместо отказа
    new ThreadFactoryBuilder().setNameFormat("orders-%d").build(),
    new ThreadPoolExecutor.CallerRunsPolicy() // перегрузка тормозит отправителя
);

// Порядок роста: сначала до core, потом в очередь, и только при полной очереди — до maximum
Пул потоков: задачи поступают в ограниченную очередь, фиксированное число воркеров берёт их в работу; при переполнении очереди задача отклоняется (reject).
Ограниченная очередь + фиксированное число воркеров; при переполнении срабатывает политика отказа.

Ловушки фабрик Executors

Удобные фабрики опасны в проде. newFixedThreadPool и newSingleThreadExecutor используют НЕограниченную LinkedBlockingQueue — при наплыве задачи копятся до OutOfMemoryError. newCachedThreadPool создаёт НЕограниченное число потоков — при всплеске исчерпает ресурсы.

Поэтому в серьёзном коде создают ThreadPoolExecutor явно — с ограниченной очередью и осознанной политикой отказа, чтобы система деградировала предсказуемо, а не падала.

Выбор размера пула

Размер зависит от природы задач. CPU-bound: примерно число доступных ядер (Runtime.availableProcessors()) ± 1 — больше потоков лишь добавят переключений. IO-bound: потоков может быть значительно больше, потому что они в основном ждут; ориентир — число ядер × (1 + время_ожидания / время_счёта).

Это отправная точка, которую подтверждают нагрузочным тестом, а не финальная формула.

Жизненный цикл и планирование

Пул нужно закрывать: shutdown() перестаёт принимать задачи и доисполняет очередь, shutdownNow() пытается прервать выполняемые и возвращает невыполненные; затем awaitTermination ждёт завершения. Незакрытый пул с не-daemon потоками не даст JVM завершиться.

submit() возвращает Future, invokeAll() запускает пачку задач. ScheduledExecutorService планирует отложенные и периодические задачи (scheduleAtFixedRate/scheduleWithFixedDelay) — современная замена java.util.Timer, который не переживал исключения и работал в одном потоке.

Как отвечать на собеседовании

Объясните, как ThreadPoolExecutor принимает задачу (core → очередь → max → политика отказа), назовите ловушку фабрик (неограниченные очередь/потоки → OOM) и почему предпочитают явный пул с ограниченной очередью. Дайте ориентиры размера для CPU- и IO-bound и не забудьте про корректный shutdown/awaitTermination.

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем