Что такое пул потоков (Thread Pool) в Java и как его создать?

Ответ

Пул потоков (Thread Pool) — это набор предсозданных потоков, готовых к выполнению задач. Вместо создания нового потока для каждой задачи, пул переиспользует существующие, что снижает накладные расходы и упрощает управление параллелизмом.

Основной интерфейс — ExecutorService.

Пример создания через Executors:

// 1. Фиксированный пул
ExecutorService fixedPool = Executors.newFixedThreadPool(4);

// 2. Кэширующий пул (создаёт потоки по мере необходимости)
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 3. Пул с одним потоком (последовательное выполнение)
ExecutorService singleThread = Executors.newSingleThreadExecutor();

// 4. Отправка задачи в пул
fixedPool.submit(() -> {
    System.out.println("Задача выполняется в " + Thread.currentThread().getName());
});

// 5. Корректное завершение работы пула
fixedPool.shutdown(); // Остановка после выполнения всех задач
// fixedPool.shutdownNow(); // Попытка немедленной остановки

Прямое создание ThreadPoolExecutor (больше контроля):

int corePoolSize = 5;
int maxPoolSize = 10;
long keepAliveTime = 60L;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,
    maxPoolSize,
    keepAliveTime,
    TimeUnit.SECONDS,
    workQueue
);

Преимущества:

  • Снижение затрат на создание/уничтожение потоков.
  • Ограничение количества одновременно работающих потоков.
  • Управление очередью задач.

Важные моменты:

  • Всегда завершайте пул (shutdown()).
  • Выбор типа пула и его параметров зависит от характера задач (CPU-bound, I/O-bound).
  • Используйте ThreadFactory для настройки создаваемых потоков (имена, приоритеты, демоны).

Ответ 18+ 🔞

А, ну это же классика, блядь! Пул потоков! Ну ты представляешь, каждый раз, когда тебе надо задачу выполнить, новый поток создавать — это как, сука, каждый раз новую тарелку брать, поел — в окно выкинул. Овердохуища мусора будет, а потом прибежит сборщик и наебнёт тебе по голове за такое расточительство.

Вот поэтому умные дядьки придумали, блядь, Thread Pool — это типа такая бригада предсозданных работяг-потоков. Сидят, блядь, на базе, курят, ждут заданий. Кинул им задачку — один отрывает жопу от стула, пошёл делать. Сделал — вернулся, опять сел ждать. Красота, а не жизнь! Накладных расходов — ноль ебать, управлять — одно удовольствие.

Главный по тарелкам тут — ExecutorService. Через него всё и делается.

Смотри, как это выглядит в коде, ёпта:

// 1. Пул фиксированный, чтоб не разбегались
ExecutorService fixedPool = Executors.newFixedThreadPool(4); // Четыре работяги, ни больше, ни меньше.

// 2. Пул кэширующий — ленивые понты. Пока задач нет — ни одного потока. Задачу кинул — поток создался. Сделал — если минуту прохлаждался, убили нахуй.
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 3. Пул с одним дауном. Один поток, всё делает по очереди, строго. Для тех, кто боится параллелизма как чёрт ладана.
ExecutorService singleThread = Executors.newSingleThreadExecutor();

// 4. А вот как кинуть задачку этим лентяям:
fixedPool.submit(() -> {
    System.out.println("Задача выполняется в " + Thread.currentThread().getName()); // Смотри, какой Вася её сделал!
});

// 5. И ВАЖНО, БЛЯДЬ! После работы бригаду распустить, а то они вечно сидеть будут, ресурсы жрать!
fixedPool.shutdown(); // Вежливо: "Ребят, новых заданий не будет, доделывайте что есть и свободны!"
// fixedPool.shutdownNow(); // Жёстко: "Всё, пиздуйте отсюда по-хорошему! А то щас по мордам дам!"

Но если ты, блядь, максималист и хочешь всё контролировать до запятой — можешь собрать пул своими руками, как конструктор:

int corePoolSize = 5;       // Постоянный штат: 5 человек всегда на базе, даже если нихуя не делают.
int maxPoolSize = 10;       // Максимальная численность: если завал, нанимаем временщиков, чтоб до 10 человек.
long keepAliveTime = 60L;   // Временщик, если 60 секунд без дела просидел — увольнение, нахуй.
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100); // Очередь задач на 100 штук. Переполнится — проблемы.
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,
    maxPoolSize,
    keepAliveTime,
    TimeUnit.SECONDS,
    workQueue
);

Чем это, сука, хорошо?

  • Не тратишь время на найм и увольнение (создание/уничтожение потоков).
  • Можешь поставить лимит на дебоширов (ограничить число потоков).
  • Все задачи в одну аккуратную очередь становятся, а не кто в лес, кто по дрова.

Но запомни, чувак:

  • Всегда shutdown() вызывай, иначе память течь будет — пипец.
  • Выбирай пул с умом: для задач, где мозги пашут (CPU-bound) — фиксированный небольшой, для задач, где много ждёшь (I/O-bound) — можно и кэширующий.
  • Хочешь, чтобы твои потоки не безликими были — юзай ThreadFactory. Задашь им имена, приоритеты, сделаешь демонами — полный контроль, блядь!