71

I am using CompletableFuture as shown below in the code. But concerning the way I should wait till all runnables finish, I found two ways and I do not know the difference between them and which one is the best practice? They are as follows:

Code:

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);

First approach to wait till all runnables finish:

this.growSeedExecutor.shutdown();
this.growSeedExecutor.awaitTermination(1, TimeUnit.DAYS);

Second approach to wait till all runnables finish:

CompletableFuture.allOf(this.growSeedFutureList).join();

Please let me know which one is recommended.

2
  • 4
    Both would work so it depends on what you want to do with the executor: if you don't need it any more the use the former - if you want to reuse it use the latter... Also in your first code snippet you only keep a reference to the last CompletableFuture...
    – assylias
    Commented Jun 8, 2015 at 10:05
  • 3
    I did not quite understand this.growSeedFutureList = . What is the type of growSeedFutureList ? Is this some new syntax of adding elements to list ? Can somebody please clarify? Is there any way to achieve this without having list ?
    – Albatross
    Commented Sep 16, 2021 at 18:35

3 Answers 3

55

If you really want to wait on all futures, you can simply call join() on each of them:

growSeedFutureList.forEach(CompletableFuture::join);

The main difference compared to using allOf() is that this will throw an exception as soon as it reaches a future completed with an exception, whereas the allOf().join() version will only throw an exception after all futures have completed (exceptionally or not).

Another small difference is that this does not create the intermediary allOf stage. Such a stage remains useful if you want to do something asynchronously after all futures have completed, instead of just waiting for all of them to complete.

The solution with the executor on the other side has several drawbacks:

  • it prevents to reuse the executor as it requires its shutdown;
  • it requires you to use that executor for all operations – it will not work with CompletableFutures that are managed in another way;
  • it does not clearly shows your intent, which is to wait for all futures to complete;
  • it is more complex to implement;
  • it does not handle exceptional completion – no exception will be thrown by awaitTermination() if one of the tasks failed.
23

Both ways are equivalent only when the executor (growSeedExecutor) is used solely for the given task. The first way may lead to following: Another tasks need parallelization, and new executor is created for each task. Some developer sees too many executors created, and decide to use single common executor, but failed to delete all executor shutdowns...

So the second way (join()) is more reliable, since is less complex. But each new future should be added to the growSeedFutureList, not assigned to.

4

A bit late for the reply but hope this code helps someone looking. This uses the common forkJoin pool executor

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);
    }
}
1
  • How would you handle exceptions in this case? For example, how to tell the main thread that one of the futures had an exception so it can take some action? Commented Mar 14 at 16:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.