Java Fork/Join pool, ExecutorService и CountDownLatch

У нас есть три разных метода многопоточности в java - Вилочный/Объединенный пул, Служба Executor и CountDownLatch

Вилка/объединение пула (http://www.javacodegeeks.com/2011/02/java-forkjoin-parallel-programming.html)

Структура Fork/Join предназначена для того, чтобы алгоритмы разделения и управления легко распараллеливались. Этот тип алгоритмов идеально подходит для проблем, которые можно разделить на две или более подзадачи того же типа. Они используют рекурсию, чтобы разбить проблему на простые задачи, пока они не станут достаточно простыми, чтобы их можно было решить напрямую. Затем решения подсуммов объединяются, чтобы дать решение исходной задачи

ExecutorService - это интерфейс, расширяющий класс Executor и представляющий асинхронное выполнение. Он предоставляет нам механизмы для управления концом и обнаружения прогресса асинхронных задач.

invokeAll(): Выполняет заданные задачи, возвращая список фьючерсов, сохраняющих их статус и результаты, когда все завершено. Future.isDone() истинно для каждого элемента возвращаемого списка.

CountDownLatch:(http://examples.javacodegeeks.com/core-java/util/concurrent/countdownlatch-concurrent/java-util-concurrent-countdownlatch-example/)

CountDownLatch используется в синхронизации, позволяя одному или нескольким потокам дождаться завершения набора операций в других потоках.

Мое предположение:

В обоих этих альтернативах конечный результат будет известен только после завершения всех задач/потоков.

Являются ли эти три альтернативы бесплатными или дополняющими друг друга?

Ответы

Ответ 1

После исследований по различным многопоточным фреймворкам за последние 3 месяца я нашел ответ на вопрос.

ExecutorService

Он прост и удобен в использовании с ограниченным контролем. Вы можете использовать его

  • Чтобы запустить параллельные независимые задачи без ожидания
  • Подождите завершения всех ваших задач.

Я предпочитаю этот вариант, когда число задач Callable/Runnable невелико, а нагромождение задач в неограниченной очереди не вызывает накапливания в памяти и ухудшает производительность системы.

Он скрывает детали низкого уровня ThreadPoolExecutor. Он не позволяет играть с другими параметрами (Bounded Queue, Rejection Handler и т.д. Для точной настройки производительности), как в ThreadPoolExectuor.

ThreadPoolExecutor

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)

Он предоставляет вам больше контроля. Помимо установки минимального и максимального потоков, вы можете установить размер очереди и сделать BlockingQueue ограниченным.

Вы можете создать свой собственный поток factory, если вам нужны ниже функции

  • Чтобы задать более описательное имя потока
  • Чтобы установить статус демона нити
  • Чтобы установить приоритет потока

Если ваше приложение ограничено количеством ожидающих задач Runnable/Callable, вы будете использовать ограниченную очередь, установив максимальную емкость. Когда очередь достигает максимальной емкости, вы можете определить RejectionHandler. Java предоставляет четыре типа обработчиков отклонения .

  • В стандартном ThreadPoolExecutor.AbortPolicy обработчик выдает исключение ExjectExceptionException после отклонения.

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

  • В ThreadPoolExecutor.DiscardPolicy задача, которая не может быть выполнена, просто удаляется.

  • В ThreadPoolExecutor.DiscardOldestPolicy, если исполнитель не закрыт, задача во главе рабочей очереди отбрасывается, а затем выполняется повторное выполнение (что может снова потерпеть неудачу, в результате чего это будет повторяться.)

CountDownLatch

CountDownLatch: эта структура позволяет потоку java ждать, пока другой набор потоков завершит выполнение своих задач.

Варианты использования:

  • Достижение максимума Parallelism: Иногда мы хотим запустить несколько потоков одновременно для достижения максимального parallelism

  • Подождите, пока N потоков не завершится до начала выполнения другого блока кода

  • Обнаружение взаимоблокировки.

Более подробная информация приведена в статье

ForkJoinPool

ForkJoinPool похож на Java ExecutorService, но с одним отличием. ForkJoinPool облегчает задачу разбиения их работы на более мелкие задачи, которые затем отправляются в ForkJoinPool. Устранение задачи происходит в ForkJoinPool, когда свободные рабочие потоки захватывают задачи из очереди занятых рабочих потоков.

public ForkJoinPool(int parallelism,
            ForkJoinPool.ForkJoinWorkerThreadFactory factory,
            Thread.UncaughtExceptionHandler handler,
            boolean asyncMode)
Creates a ForkJoinPool with the given parameters.

Параметры:

parallelism - уровень parallelism. Для значения по умолчанию используйте Runtime.availableProcessors().

factory - factory для создания новых потоков. Для значения по умолчанию используйте defaultForkJoinWorkerThreadFactory.

обработчик - обработчик для внутренних рабочих потоков, которые завершаются из-за неустранимых ошибок

asyncMode - если true, устанавливает локальный режим планирования первого в первом порядке для разветвленных задач, которые никогда не соединяются.

Относительно основного запроса:

Вы можете использовать рамки ExecutorService.invokeAll() или CountDownLatch или ForkJoinPool. Все эти рамки дополняют друг друга с различной степенью детализации для управления выполнением задач с высокого уровня на низкий уровень.

EDIT:

Посмотрите на связанные вопросы SE:

В чем преимущества использования ExecutorService?

Java-приложение Fork/Join vs ExecutorService - когда использовать, который?