#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
.