Неупорядоченные возвраты из Java-фьючерсов

#java #concurrency #future

#java #параллелизм #будущее

Вопрос:

Единственная модель, которую я могу придумать для запуска нескольких похожих процессов (SIMD) с использованием Java Futures ( java.util.concurrent.Future<T> ), заключается в следующем:

 class Job extends Callable<T> {
  public T call() {
    // ...
  }
}
List<Job> jobs = // ...
List<Future<T>> futures = ExecutorService.invokeAll(jobs);
for (Future<T> future : futures) {
  T t = future.get();
  // Do something with t ...
}
  

Проблема с этой моделью заключается в том, что если выполнение задания 0 занимает много времени, но задания 1, 2 и 3 уже завершены, for цикл будет ожидать получения возвращаемого значения из задания 0.

Существует ли какая-либо модель, которая позволяет мне получать каждый Future результат по мере его поступления без простого вызова Future.isDone() и напряженного ожидания (или вызова Thread.sleep() ), если ни один из них еще не готов?

Комментарии:

1. Я бы сказал, что ответ, на который вы намекнули, заключается в использовании метода isDone () в качестве защиты для метода get () . Похоже, что сам ExecutorService выиграл бы от возможности предоставлять вам завершенные задания, но я не думаю, что это вариант. Вы не можете что-то сделать в задании, которое вызывает обратный вызов при его завершении?

Ответ №1:

Вы можете попробовать ExecutorCompletionService :

http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ExecutorCompletionService.html

Вы бы просто отправили свои задачи и вызывали take, пока не получили все фьючерсы.

Комментарии:

1. Я не был в курсе ExecutorCompletionService . Мне нужно будет взглянуть на это.

Ответ №2:

Рассмотрите возможность использования ListenableFuture из Guava. Они позволяют вам в основном добавлять продолжение для выполнения по завершении future.

Ответ №3:

Почему бы вам не добавить в задание то, что вы хотите выполнить?

 class Job extends Runnable {
  public void run() {
    // ...
    T result = ....
    // do something with the result.
  }
}
  

Таким образом, он обработает результат, как только он станет доступен, одновременно. 😉

Комментарии:

1. Это не сработает, если получателем является графический интерфейс Swing. Я полагаю, вы могли бы использовать SwingUtilities.invokeLater() , но предположим, вы хотите обновить индикатор выполнения — становится сложнее.

Ответ №4:

CompletionService Можно опросить доступные результаты.

Однако, если все, что вам нужно, это результаты по мере их поступления, мы написали AsyncCompleter , который абстрагирует детали использования службы завершения. Это позволяет отправлять Iterable<Callable<T>> задания и возвращает Iterable<T> результаты, которые блокируются на next() и возвращают результаты в порядке завершения.

Комментарии:

1. Смотрите ответ Томаса Юнгблута выше. ExecutorCompletionService Может делать то же самое, что и ваш AsyncCompleter .

2. AsyncCompleter оборачивает ExecutorCompletionService под обложками и обрабатывает все . Интерфейс прост: AsyncCompleter.invokeAll(Iterable<Callable<T> jobs) который возвращает повторяющийся<T> результат, который блокируется при вызовах next(). Он не объявляет InterruptedExceptions, вы не выполняете функцию poll(). Короче говоря, это намного проще в использовании, чем CompletionService .