Каков рекомендуемый способ ожидания завершения завершаемых будущих потоков?
Я использую CompletableFuture
как показано ниже в коде. Но что касается того, как я должен ждать, пока не закончатся все работающие объекты, я нашел два пути, и я не знаю разницу между ними, и какой из них является лучшим? Они заключаются в следующем:
Код:
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
Первый подход, чтобы подождать, пока завершатся все runnables:
this.growSeedExecutor.shutdown();
this.growSeedExecutor.awaitTermination(1, TimeUnit.DAYS);
Второй подход - дождаться завершения всех работ:
CompletableFuture.allOf(this.growSeedFutureList).join();
Пожалуйста, дайте мне знать, какой из них рекомендуется.
3 ответа
Оба способа эквивалентны только тогда, когда исполнитель (growSeedExecutor) используется исключительно для данной задачи. Первый способ может привести к следующему: другие задачи требуют распараллеливания, и для каждой задачи создается новый исполнитель. Некоторые разработчики видят, что создано слишком много исполнителей, и решают использовать одного общего исполнителя, но не смогли удалить все выключения исполнителя...
Таким образом, второй способ (join()) является более надежным, поскольку он менее сложен. Но каждое новое будущее должно быть добавлено в GrowSeedFutureList, а не назначено.
Если вы действительно хотите ждать по всем фьючерсам, вы можете просто позвонить join()
на каждом из них:
growSeedFutureList.forEach(CompletableFuture::join);
Основное отличие по сравнению с использованием allOf()
является то, что это вызовет исключение, как только оно достигнет будущего, завершенного с исключением, тогда как allOf().join()
версия выдаст исключение только после того, как все фьючерсы будут завершены (в исключительном порядке или нет).
Другое небольшое отличие состоит в том, что это не создает посредника allOf
этап. Такой этап остается полезным, если вы хотите сделать что-то асинхронно после завершения всех фьючерсов, вместо того, чтобы просто ждать, пока все они завершатся.
Решение с исполнителем на другой стороне имеет несколько недостатков:
- предотвращает повторное использование исполнителя, так как требует его закрытия;
- он требует, чтобы вы использовали этот исполнитель для всех операций - он не будет работать с
CompletableFuture
ы, которые управляются по-другому; - он не ясно показывает ваше намерение, а именно ожидание завершения всего будущего;
- это сложнее реализовать;
- это не обрабатывает исключительное завершение - никакое исключение не будет брошено
awaitTermination()
если одна из задач не удалась.
Немного поздно для ответа, но надеюсь, что этот код поможет кому-то искать. Здесь используется общий исполнитель пула forkJoin.
package com.company;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String args[]){
List<CompletableFuture> futureList=new ArrayList<>();
for(int i=0;i<10;i++) {
futureList.add(CompletableFuture.supplyAsync(()->getThreadName()).thenAccept(name->printThreadName(name)));
}
futureList.forEach(CompletableFuture::join);
}
static String getThreadName(){
String threadDetails=Thread.currentThread().getName();
System.out.println("thread deteails::::"+threadDetails);
return threadDetails;
}
static void printThreadName(String value){
System.out.println("thread string value::"+value);
}
}