Java

ExecutorService появился в Java 1.5 и предназначен для управления пулом потоков.

Есть несколько стандартных реализаций, которые можно получить из простой фабрики Executors:

  1. newFixedThreadPool
  2. newWorkStealingPool
  3. newSingleThreadExecutor
  4. newCachedThreadPool
  5. newSingleThreadScheduledExecutor
  6. newScheduledThreadPool

Догадаться об особенностях реализаций сервиса можно исходя из названий методов. В статье рассмотрен пример использования FixedThreadPool.

Для запуска приложения будем использовать Spring Boot:

Main.java

Предположим, что клиентскому классу необходимо выполнить много работы, которую можно разделить на несколько независимых подзадач. Например, необходимо получить html-страницы по списку URL-адресов или обработать большой список строк:

Работа будет заключаться в приведении строк к верхнему регистру

Сохраним список работ, который необходимо провести, в виде List

Каждый элемент списка – это элементарная работа, реализация функционального интерфейса, которую необходимо выполнить запустив метод call. Но вместо ручного вызова мы создадим сервис, который будет управлять пулом потоков:

В параметре метода newFixedThreadPool мы указали значение 4, что задает размер будущему пулу потоков.

workService – это сервис Spring, который делает работу и выводит некоторую информацию для отладки:

Для наглядности добавим паузу при выполнении задачи в 1 секунду. Если задача будет выполняться слишком быстро, то executorService может не выделять дополнительные потоки для обработки и выполнит все задачи в одном потоке.

Запускаем выполнение списка задач методом executorService.invokeAll(), который возвращает список Future объектов. Сразу смапим все Future в интересующие нас String и выведем на экран:

Получившийся код класса клиента:

Запустим пример и посмотрим на вывод:

Из логов можно сделать следующие заключения:

  1. Сначала одновременно запустилось 4 потока на выполнение работ work1, work2, work3, work4
  2. Через секунду все 4 потока доделали работу и принялись выполнять work5, work6, work7, work8
  3. Еще через секунду потоки закончили выполнение второй четверки работ и два потока принялись выполнять последние две работы work9 и work10
  4. Получившийся список строк, как и список Future, является отсортированным, хотя это нигде не регламентировалось. Полагаю, не стоит надеяться, что список всегда будет отсортирован.
  5. На выполнение всех задач в 4 потока потрачено 3 секунды. 1 секунда – на первую четверку задач, 2 секунда – на вторую четверку задач, 3 секунда – на оставшиеся две задачи.

Архив с исходниками можно посмотреть на GitHub.

Пример использования ExecutorService