Найти в Дзене
Java & Kotlin dev

Глубокий взгляд внутрь Executor в Java: Работа ThreadPoolExecutor и Управление Потоками

Оглавление

В Java интерфейс Executor является частью пакета java.util.concurrent, предоставляющего удобные средства для работы с параллелизмом и асинхронным выполнением задач. Executor является простым интерфейсом, предназначенным для выполнения задач в фоновом режиме. Он определен следующим образом:

Executor предоставляет всего один метод execute, который принимает объект типа Runnable и выполняет его асинхронно в некотором потоке. Объект Runnable представляет собой задачу, которую нужно выполнить.

Один из наиболее распространенных классов, реализующих интерфейс Executor, это ThreadPoolExecutor. Рассмотрим, как ThreadPoolExecutor устроен внутри.

ThreadPoolExecutor

ThreadPoolExecutor представляет собой реализацию Executor, предназначенную для управления пулом потоков. Он имеет несколько конструкторов, но один из наиболее часто используемых выглядит следующим образом:

-2

Давайте разберем каждый из параметров:

  • corePoolSize: Минимальное количество потоков в пуле, которые будут созданы и поддерживаемы в работоспособном состоянии даже в случае отсутствия задач для выполнения.
  • maximumPoolSize: Максимальное количество потоков в пуле. Если текущее количество потоков превышает corePoolSize, и есть больше задач, чем может быть немедленно выполнено, будут созданы дополнительные потоки до тех пор, пока число потоков не достигнет maximumPoolSize.
  • keepAliveTime: Время, в течение которого "лишние" потоки (которые больше corePoolSize) будут ждать новые задачи перед завершением.
  • unit: Единица измерения времени для параметра keepAliveTime.
  • workQueue: Очередь задач, ожидающих выполнения. Это структура данных, в которой будут храниться задачи, ожидающие своей очереди на выполнение.
  • handler: Обработчик, который будет вызван, если невозможно принять или выполнить задачу. Например, если пул потоков находится в состоянии насыщения (все потоки заняты, и очередь полна).

ThreadPoolExecutor создает и управляет потоками, обеспечивая их выполнение по мере поступления задач. Внутренне он содержит механизм для запуска, приостановки и завершения потоков в зависимости от нагрузки.

Работа ThreadPoolExecutor

  1. Принятие задачи (execute): При вызове метода execute, ThreadPoolExecutor пытается использовать один из существующих потоков для выполнения задачи. Если количество потоков меньше corePoolSize, то создается новый поток. Если corePoolSize превышено, но не превышено maximumPoolSize, то создается новый поток. Если и corePoolSize, и maximumPoolSize превышены, задача добавляется в очередь (если она не переполнена).
  2. Очередь задач (workQueue): Если потоков больше, чем corePoolSize, а текущее количество задач превышает число активных потоков, то задачи добавляются в очередь. Это позволяет управлять большими пиковыми нагрузками.
  3. Жизненный цикл потоков: Потоки в пуле исполняют задачи и остаются активными, пока не проходит время keepAliveTime, после чего "лишние" потоки могут быть завершены, если нет задач для выполнения.
  4. Обработка исключений (RejectedExecutionHandler): Если пул находится в состоянии насыщения и не может принять новую задачу, вызывается обработчик RejectedExecutionHandler. По умолчанию используется стратегия AbortPolicy, которая просто бросает исключение.

Давайте рассмотрим несколько подробных примеров использования ThreadPoolExecutor в Java для решения различных задач.

Пример 1: Выполнение Простых Задач

В этом примере мы создадим ThreadPoolExecutor для выполнения нескольких простых задач.

-3

Пример 2: Работа с Callable и Future

Используем ThreadPoolExecutor для выполнения задач, возвращающих результаты, с использованием интерфейсов Callable и Future.

-4

Пример 3: Использование RejectedExecutionHandler

Используем ThreadPoolExecutor с собственным RejectedExecutionHandler для обработки отклоненных задач.

-5

Эти примеры демонстрируют различные сценарии использования ThreadPoolExecutor, начиная от простых задач и заканчивая работой с отклоненными задачами. Управление потоками в таком пуле предоставляет гибкость и оптимизацию процесса выполнения задач в многозадачной среде Java.