← Назад к вопросам

Что такое ThreadPool?

2.0 Middle🔥 241 комментариев
#JVM и управление памятью#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

ThreadPool: управление потоками в Java

ThreadPool (пул потоков) — это коллекция переиспользуемых потоков, которые ждут назначения задач. Вместо создания нового потока для каждой задачи, мы используем уже созданные потоки из пула, что значительно улучшает производительность.

Проблема без ThreadPool

// ❌ Плохо — создаём новый поток на каждую задачу
public void processRequests(List<Task> tasks) {
    for (Task task : tasks) {
        new Thread(() -> {
            task.execute();
        }).start();
    }
}

// Проблемы:
// - Создание потока дорого (требует памяти и времени)
// - Неограниченное количество потоков → OutOfMemoryError
// - Плохая производительность (context switching)
// - Сложно управлять потоками

ThreadPool решение

// ✅ Хорошо — используем пул потоков
ExecutorService executor = Executors.newFixedThreadPool(10);

public void processRequests(List<Task> tasks) {
    for (Task task : tasks) {
        executor.submit(() -> {
            task.execute();
        });
    }
}

// Преимущества:
// - Потоки переиспользуются
// - Ограниченное количество потоков
// - Очередь задач (queue)
// - Управление жизненным циклом

Как работает ThreadPool

┌─────────────────────────────────────────┐
│          ThreadPool (10 потоков)        │
├─────────────────────────────────────────┤
│ [Thread-1] [Thread-2] ... [Thread-10]   │
│ (работают)                              │
└──────────┬──────────────────────────────┘
           │
      ┌────▼────────────────┐
      │   Queue (FIFO)      │
      │ [Task-1]            │
      │ [Task-2]            │
      │ [Task-3]            │
      │ ... (до 100 задач)  │
      └─────────────────────┘

Если поток свободен → берёт следующую задачу из очереди
Если все потоки заняты → задача ждёт в очереди
Если очередь переполнена → выполняется policy (reject/wait/etc)

Типы ExecutorService

1. FixedThreadPool — фиксированное количество потоков

// Самый частый выбор
ExecutorService executor = Executors.newFixedThreadPool(10);

// Создаёт 10 потоков, которые живут вечно
// Новые задачи добавляются в очередь
// Идеально для:
// - Обработки HTTP запросов
// - Параллельных вычислений
// - Фоновых задач с ограничением нагрузки

for (int i = 0; i < 100; i++) {
    final int id = i;
    executor.submit(() -> {
        System.out.println("Task " + id + " on thread " + Thread.currentThread().getName());
        // Поток возвращается в пул
    });
}

2. CachedThreadPool — создаёт потоки по необходимости

// Гибкий пул
ExecutorService executor = Executors.newCachedThreadPool();

// Создаёт новые потоки по необходимости
// Переиспользует неиспользуемые потоки (timeout 60 сек)
// Идеально для:
// - Коротких асинхронных задач
// - Когда нагрузка непредсказуема
// - Когда задачи могут блокировать друг друга

// ⚠️ Опасность: может создать слишком много потоков

3. SingleThreadExecutor — один поток

// Для последовательной обработки задач
ExecutorService executor = Executors.newSingleThreadExecutor();

// Все задачи выполняются в одном потоке
// Гарантирует FIFO порядок
// Идеально для:
// - Обработки событий
// - Где важен порядок выполнения
// - Избежание race conditions

executor.submit(() -> System.out.println("Task 1")); // Выполнится первой
executor.submit(() -> System.out.println("Task 2")); // Выполнится второй

4. ScheduledExecutorService — планирование задач

ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);

// Выполнить один раз через 2 секунды
executor.schedule(() -> {
    System.out.println("Delayed task");
}, 2, TimeUnit.SECONDS);

// Выполнить периодически (начало через 1 сек, повтор каждые 2 сек)
executor.scheduleAtFixedRate(() -> {
    System.out.println("Periodic task");
}, 1, 2, TimeUnit.SECONDS);

// Выполнить периодически (задержка между завершением и следующим стартом)
executor.scheduleWithFixedDelay(() -> {
    System.out.println("Task with delay");
}, 1, 2, TimeUnit.SECONDS);

Конфигурация ThreadPool

// Использование ThreadPoolExecutor для полного контроля
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                      // corePoolSize — минимум потоков
    10,                     // maximumPoolSize — максимум потоков
    60,                     // keepAliveTime
    TimeUnit.SECONDS,       // время жизни неиспользуемого потока
    new LinkedBlockingQueue<>(100),  // очередь для задач
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()  // policy при переполнении очереди
);

// Policy при переполнении очереди:
// - AbortPolicy: выброс исключения (по умолчанию)
// - CallerRunsPolicy: вызывающий поток выполняет задачу
// - DiscardPolicy: отброс задачи
// - DiscardOldestPolicy: отброс самой старой задачи в очереди

Практический пример

public class ApiService {
    private final ExecutorService executor = Executors.newFixedThreadPool(20);
    
    // Обработка HTTP запроса в отдельном потоке
    public void handleRequest(HttpRequest request, HttpResponse response) {
        executor.submit(() -> {
            try {
                // Обработка может быть долгой (DB запрос, внешний API)
                String result = processRequest(request);
                response.write(result);
            } catch (Exception e) {
                response.writeError(e);
            }
        });
    }
    
    // Корректное завершение
    public void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

Жизненный цикл

ExecutorService executor = Executors.newFixedThreadPool(5);

// 1. Добавление задач (активно)
executor.submit(task1);
executor.submit(task2);

// 2. Остановка новых задач
executor.shutdown();  // Мягкое завершение
// или
executor.shutdownNow();  // Принудительное завершение (может привести к потере)

// 3. Ожидание завершения
boolean finished = executor.awaitTermination(5, TimeUnit.MINUTES);
if (!finished) {
    System.err.println("Pool did not terminate");
}

Когда использовать ThreadPool

✅ Сервер обработки HTTP запросов ✅ Параллельные вычисления ✅ Фоновые асинхронные задачи ✅ Периодические scheduled задачи ✅ Обработка событий из queue ✅ Spring Boot (автоматически использует ThreadPool)

❌ Вычисления в одном потоке ❌ Стартап приложения ❌ Быстрые синхронные операции

Мониторинг ThreadPool

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);

// Статистика
System.out.println("Active threads: " + executor.getActiveCount());
System.out.println("Core pool size: " + executor.getCorePoolSize());
System.out.println("Max pool size: " + executor.getMaximumPoolSize());
System.out.println("Queue size: " + executor.getQueue().size());
System.out.println("Completed tasks: " + executor.getCompletedTaskCount());

ThreadPool — это фундаментальная концепция для масштабируемых и производительных Java приложений.

Что такое ThreadPool? | PrepBro