Problem
In Java Official Doc, it says
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete. If any of the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so, with a CompletionException holding this exception as its cause.
The doc doesn't specify the case when multiple given CompletableFutures complete exceptionally. For example, in the following code snippet, what will the exception and its cause be if c1, c2, c3 all complete exceptionally?
CompletableFuture.allOf(c1, c2, c3)
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
My experiment 1
Create a completableFuture signal_1 and signal_2 that both completes exceptionally fast. The output shows signal_1 gets passed to .whenComplete() as the cause of exception.
package com.company;
import java.util.concurrent.*;
public class Main {
private static void runTasks(int i) {
System.out.printf("-- input: %s --%n", i);
CompletableFuture<Void> signal_1 = new CompletableFuture<>();
signal_1.completeExceptionally(new RuntimeException("Oh noes!"));
CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);
CompletableFuture.allOf(signal_1, signal_2)
.thenApplyAsync(justVoid -> {
final int num = signal_2.join();
System.out.println(num);
return num;
})
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
.thenApplyAsync(input -> input * 3)
.thenAccept(System.out::println);
}
public static void main(String[] args) {
runTasks(0);
}
}
Output
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!
Process finished with exit code 0
My experiment 2
Added a 3 second sleep before signal_1 completes exceptionally, so signal_1 should completes after signal_2. However, the output still shows signal_1 gets passed to .whenComplete() as the cause of exception.
package com.company;
import java.util.concurrent.*;
public class Main {
static ExecutorService customExecutorService = Executors.newSingleThreadExecutor();
private static void runTasks(int i) {
System.out.printf("-- input: %s --%n", i);
CompletableFuture<Void> signal_1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException("Oh noes!");
}, customExecutorService);
CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);
CompletableFuture.allOf(signal_1, signal_2)
.thenApplyAsync(justVoid -> {
final int num = signal_2.join();
System.out.println(num);
return num;
})
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
.thenApplyAsync(input -> input * 3)
.thenAccept(System.out::println);
}
public static void main(String[] args) {
runTasks(0);
customExecutorService.shutdown();
}
}
Output
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!
Process finished with exit code 0