Waiting on a list of Future
You can use a CompletionService to receive the futures as soon as they are ready and if one of them throws an exception cancel the processing. Something like this:
Executor executor = Executors.newFixedThreadPool(4);CompletionService<SomeResult> completionService = new ExecutorCompletionService<SomeResult>(executor);//4 tasksfor(int i = 0; i < 4; i++) { completionService.submit(new Callable<SomeResult>() { public SomeResult call() { ... return result; } });}int received = 0;boolean errors = false;while(received < 4 && !errors) { Future<SomeResult> resultFuture = completionService.take(); //blocks if none available try { SomeResult result = resultFuture.get(); received ++; ... // do something with the result } catch(Exception e) { //log errors = true; }}
I think you can further improve to cancel any still executing tasks if one of them throws an error.
If you are using Java 8 then you can do this easier with CompletableFuture and CompletableFuture.allOf, which applies the callback only after all supplied CompletableFutures are done.
// Waits for *all* futures to complete and returns a list of results.// If *any* future completes exceptionally then the resulting future will also complete exceptionally.public static <T> CompletableFuture<List<T>> all(List<CompletableFuture<T>> futures) { CompletableFuture[] cfs = futures.toArray(new CompletableFuture[futures.size()]); return CompletableFuture.allOf(cfs) .thenApply(ignored -> futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList()) );}
Use a CompletableFuture
in Java 8
// Kick of multiple, asynchronous lookups CompletableFuture<User> page1 = gitHubLookupService.findUser("Test1"); CompletableFuture<User> page2 = gitHubLookupService.findUser("Test2"); CompletableFuture<User> page3 = gitHubLookupService.findUser("Test3"); // Wait until they are all done CompletableFuture.allOf(page1,page2,page3).join(); logger.info("--> " + page1.get());