Reputation: 2034
I encountered a strange situation. I'm fiddling with CompletableFuture
and when running the following code I have unexpected results:
public static void main(String[] args) {
CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<Object>>>>>> completableFutureCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("first");
return CompletableFuture.supplyAsync(() -> {
System.out.println("second");
return CompletableFuture.supplyAsync(() -> {
System.out.println("third");
return CompletableFuture.supplyAsync(() -> {
System.out.println("fourth");
return CompletableFuture.supplyAsync(() -> {
System.out.println("fifth");
return CompletableFuture.completedFuture(null);
});
});
});
});
});
completableFutureCompletableFuture.get();
}
No exception is thrown (even when using exceptionally
) and what I see is that the console output is
first
second
third // appears sometimes
Now, obviously this code has no real production value but this is a representation of a case where your code has an unknown number of nestings where each, or some of them, create CompleteableFutures
which won't be executed.
Any explanation (and example on how to fix) would be greatly appreciated
Upvotes: 5
Views: 2754
Reputation: 2370
It happens because your CompletableFuture are executed asynchronously but your program terminates before the fifth call happens (I assume you ran it in a single main and returned just after creating your futures).
As you may not know how many Future are stacked in your Future (due to type erasure). You may want to perform a recursive .get().
See :
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<?> futures = getFutures();
recursiveGet(futures);
System.out.println("finished");
}
public static CompletableFuture<?> getFutures() {
CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<Object>>>>>> compositeCompletable = CompletableFuture.supplyAsync(() -> {
System.out.println("first");
return CompletableFuture.supplyAsync(() -> {
System.out.println("second");
return CompletableFuture.supplyAsync(() -> {
System.out.println("third");
return CompletableFuture.supplyAsync(() -> {
System.out.println("fourth");
return CompletableFuture.supplyAsync(() -> {
System.out.println("fifth");
return CompletableFuture.completedFuture(null);
});
});
});
});
});
return compositeCompletable;
}
public static void recursiveGet(Future<?> future) throws InterruptedException, ExecutionException{
Object result = future.get();
if(result instanceof Future){
recursiveGet((Future<?>) result);
}
}
which returns
first
second
third
fourth
fifth
finished
Upvotes: 5
Reputation: 6075
Just tested this and it works. I think the reason why is not working for you is because you run in in a main method and you did not wait to complete. I did a Thread.sleep(1000)
after your code and it worked. The best way would be to wai for termination: completableFutureCompletableFuture.get().get().get().get().get()
Upvotes: 3
Reputation: 8044
The reason why this doesn't work is because in your simple test the VM exits before all tasks are completed.
When you call completableFutureCompletableFuture.get()
only the first nesting of the futures is guaranteed to have finished. The VM exits, and all threads get killed.
In other words, the first nested future could still be "uncompleted" as its thread might still be busy. However, when you try to get its result with get
it will of course wait until it completed and it will work as expected. Just try:
completableFutureCompletableFuture.get().get().get().get().get()
... then you force all futures to have completed and everything works as expected.
Upvotes: 8