Reputation: 3215
I'm comparing the behaviour of CompletableFuture.supplyAsync() in the two cases in which I set a custom ExecutorService or I want my Supplier to be executed by the default executor (if not specified) which is ForkJoinPool.commonPool()
Let's see the difference:
public class MainApplication {
public static void main(final String[] args) throws ExecutionException, InterruptedException {
Supplier<String> action1 = () -> {
try {
Thread.sleep(3000);
}finally {
return "Done";
}
};
Function<String, String> action2 = (input) -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
return input + "!!";
}
};
final ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture.supplyAsync(action1, executorService)
.thenApply (action2)
.thenAccept (res -> System.out.println(res));
System.out.println("This is the end of the execution");
}
}
In this case I'm passing executorService to my supplyAsync() and it prints:
This is the end of the execution
Done!!
So "Done" gets printed after the end of the main execution.
BUT if I use instead:
CompletableFuture.supplyAsync(action1)
so I don't pass my custom executorService and the CompletableFuture class uses under the hood the ForkJoinPool.commonPool() then "Done" is not printed at all:
This is the end of the execution
Process finished with exit code 0
Why?
Upvotes: 2
Views: 3183
Reputation: 734
In both cases when you do
CompletableFuture.supplyAsync(action1, executorService)
.thenApply (action2)
.thenAccept (res -> System.out.println(res));
you don't wait for task completition. But then you program is going to exit and there is differences how common fork join pool:
ForkJoinPool.commonPool()
and regular executor service:
final ExecutorService executorService = Executors.newFixedThreadPool(4);
..react on attempt to call System.exit(...) equivalent.
This is what doc says about fork join common pool, you should point attention to that:
However this pool and any ongoing processing are automatically terminated upon program System.exit(int). Any program that relies on asynchronous task processing to complete before program termination should invoke commonPool().awaitQuiescence, before exit.
That is link to ExecutorService docs, you may point attention to:
The shutdown() method will allow previously submitted tasks to execute before terminating
I think that may be a difference you asking about.
Upvotes: 2
Reputation: 21124
ForkJoinPool
uses daemon threads that does not prevent JVM from exiting. On the other hand the threads in the ExecutorService created by Executors are non-daemon threads, hence it keeps JVM from exiting until you explicitly shutdown the thread pool.
Also notice that in your example you need to shutdown the pool at the end in order to terminate the JVM.
executorService.shutdown();
So, one solution would be to keep the main thread waiting for few seconds until your computation is completed like so,
Thread.sleep(4000);
Upvotes: 1