ExecutorService
Создавать новый поток на каждую задачу дорого. Пул потоков (ExecutorService) держит набор готовых потоков и раздаёт им задачи:
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> doWork());
pool.shutdown(); // важно остановить пул, когда задачи закончились
Пул переиспользует потоки вместо создания новых на каждый вызов, а размер пула ограничивает, сколько задач выполняется параллельно — остальные ждут очередь.
Копнуть глубже
Виды пулов из Executors:
| Фабричный метод | Что делает |
|---|---|
newFixedThreadPool(n) | ровно n потоков, остальные в очереди |
newCachedThreadPool() | поток на каждую задачу; лишние потоки умирают через 60с |
newSingleThreadExecutor() | один поток, задачи выполняются по очереди |
newScheduledThreadPool(n) | для задач с задержкой или по расписанию |
Как выбрать размер пула. Нет универсального ответа, зависит от задачи:
- CPU-intensive задачи (вычисления):
n = CPU_coresилиCPU_cores + 1 - IO-intensive (HTTP, БД): потоки большую часть времени ждут — можно
10–50 × CPU_cores
ThreadPoolExecutor напрямую — когда нужна тонкая настройка:
ThreadPoolExecutor pool = new ThreadPoolExecutor(
4, // corePoolSize — постоянные потоки
16, // maximumPoolSize — максимум при нагрузке
60, TimeUnit.SECONDS, // keepAlive для лишних потоков
new LinkedBlockingQueue<>(1000) // очередь задач (ограниченная!)
);
// Если очередь переполнена — политика отказа
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
🎤 Закрыл тему, если можешь объяснить:
• зачем нужен пул потоков и чем он лучше создания `new Thread()` на каждую задачу;
• чем отличаются fixed vs cached пулы и как выбрать размер (если дошёл до 2-го слоя).
• чем отличаются fixed vs cached пулы и как выбрать размер (если дошёл до 2-го слоя).