Issue
I have a custom ExecutorService,
class CustomExecutorService implements ExecutorService {
ExecutorService delegate;
public CustomExecutorService(ExecutorService delegate){
this.delegate = delegate;
}
// All methods are just delegated to the delegate except execute
public void execute(Runnable command) {
this.delegate.execute(wrapRunnable(command));
}
private Runnable wrapRunnable(final Runnable command) {
return () -> {
try {
command.run();
} finally {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finalizer done.");
}
};
}
}
Now this is my test code,
ExecutorService executorService = new CustomExecutorService(Executors.newCachedThreadPool());
CompletableFuture.runAsync(() -> System.out.println("Command done."), executorService).get();
System.out.println("Main Thread Done.");
Now I expect the following output to happen,
Command done.
Finalizer done. //After a wait of 2 seconds
Main Thread Done.
However what is happening is,
Command done.
Main Thread Done.
And it is not even waiting for the finally
block and main thread is exiting. I am not able to explain this behaviour as CompletableFuture#get
is supposed to be blocking, but it seems it is not waiting for the finallly
block.
I am aware that finally
is not guaranteed to be executed if the finally
is going to be executed by a daemon thread and all other non-daemon threads exit before finally
is called. However ExecutorService threads are non-daemon threads, so finally
should be executed.
I am running this on corretto-11.0.11
Solution
When you run CompletableFuture.runAsync
it wraps your Runnable
in a task (AsyncRun
in the source) which runs the runnable, then marks the CompletableFuture as complete. This is what's passed to the executor.
In your executor you are wrapping that task so the future is already marked complete by the time your finally
block runs, which is why get()
returns immediately after that point.
Answered By - Calum
Answer Checked By - Candace Johnson (JavaFixing Volunteer)