Создание пользовательского java.util.concurrent.ForkJoinTask‹V›

В настоящее время я оцениваю различные решения параллелизма для решения бизнес-проблемы. Вариант использования сродни алгоритму «смущающе-параллельного».

По сути, для одного запроса пользователя нам нужно получить данные из нескольких разных источников данных, прежде чем вычислять ответ. В настоящее время все 3 вызова DAO выполняются последовательно, но не имеют взаимозависимостей, поэтому их можно выполнять параллельно.

Решения, реализованные на данный момент:

  • java.util.concurrent.ExecutorService с использованием Callables и Futures
  • org.springframework.scheduling.annotation.Async, чтобы позволить Spring управлять пулом потоков, но по-прежнему позволяет мне делать асинхронные вызовы
  • Akka (считается излишним) для нашего относительно простого варианта использования

Последним фреймворком, который я хотел оценить, был фреймворк Java ForkJoin, я вижу несколько примеров использования RecursiveTasks, но мой вариант использования не является рекурсивным по своей природе, поэтому не соответствует модели: если задача достаточно мала, сделайте это, иначе разделите ее и рекурсивно вызовите то же самое. метод (т.е. разделяй и властвуй)

Мой вариант использования: разделить задачу на 3 задачи. разветвите все 3 и снова присоединитесь. Является ли это допустимым вариантом использования для реализации ForkJoin? Или я должен придерживаться общей реализации ExecutorService.


person user1498180    schedule 04.07.2012    source источник


Ответы (4)


Преимущество ExecutorService заключается в том, что он может поддерживать пул потоков. Таким образом, в вашем случае для нескольких последовательных вызовов потоки будут использоваться повторно, что экономит несколько циклов ОС для остановки и создания новых потоков.

Еще одно преимущество ForkJoinPool заключается в том, что он может «воровать» работу. Насколько я знаю, это означает, что он позволяет одному потоку, завершившему задачу, немедленно выполнить другую задачу с гораздо меньшими накладными расходами, чем ExecutorService.

В вашем случае преимущество ForkJoinPool кажется минимальным.

person GreyFairer    schedule 04.07.2012

Как вы уже упоминали, в вашем случае использования нет никакой рекурсии. Если вам просто нужно запустить 3 задачи параллельно и дождаться их завершения, ExecutorService.invokeAll кажется самым простым решением.

person casablanca    schedule 04.07.2012

Только для 3 параллельных задач самый простой способ — запустить 3 потока и дождаться их завершения с помощью thread.join().

person Alexei Kaigorodov    schedule 04.07.2012
comment
Лично я считаю, что использование низкоуровневого API малоэффективно, если только вам действительно не нужен низкоуровневый элемент управления, что здесь не так. Исполнители делают код короче (немного) и менее подверженным ошибкам. - person assylias; 04.07.2012
comment
Создание и уничтожение потоков — довольно интенсивная операция для ОС. Если вы используете ThreadPoolExecutor, он будет повторно использовать потоки для последовательных вызовов пользователя. - person GreyFairer; 04.07.2012
comment
@greyfairer: переработка имеет смысл, если клиентских запросов много. Возможно, это так, но автор темы не акцентировал на этом внимание. - person Alexei Kaigorodov; 05.07.2012

Определенно не ForkJoinPool.

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

Задачи также не должны выполнять блокирующий ввод-вывод... от 100 до 10000 основных вычислительных шагов.

Поэтому, вероятно, лучше использовать либо управление параллелизмом контейнера, либо ExecutorServices с фьючерсами.

person zubrabubra    schedule 05.07.2012