Using graceful shutdown with async task ran with

CompletableFuture
                .runAsync(() -> {

                }, executor)

Using @EnableScheduling and the executor applicationTaskExecutor the graceful shutdown don't take in account the currently running tasks, when the server shutdown the task is silently aborted (no InteruptedException).

Using a custom Executor:

ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(2);
        taskExecutor.setMaxPoolSize(2);
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(1);
        taskExecutor.setVirtualThreads(true);
        taskExecutor.initialize();

The graceful shutdown wait 1 second before the end of the task, and the log [WARN] - Forcing shutdownNow() - Tasks did not stop in time. is displayed. But no InterruptedException is thrown in the Runnable.

To have an InterruptedException I should create a scheduler like that

 ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(2);
        taskExecutor.setMaxPoolSize(2);
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(1);
        taskExecutor.setVirtualThreads(true);
        taskExecutor.initialize();

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                log.info("Waiting for executor to terminate...");
                ThreadPoolExecutor executor = taskExecutor.getThreadPoolExecutor();
                if (executor != null) {
                    if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
                        log.warn("Forcing shutdownNow() - Tasks did not stop in time.");
                        executor.shutdownNow();
                    }
                }
            } catch (InterruptedException e) {
                log.error("Shutdown hook interrupted!", e);
                Thread.currentThread().interrupt();
            }
        }));

Doing that the graceful shutdown wait 1 second and stop the task with an InterruptedException.

The goal of the InterruptedException is to be able to log the stopped task, to know that something should be retry.

Why the default ThreadPoolTaskExecutor don't offer a way to have an InterruptedException. Is it possible to add this feature? To avoid to write it manually ?

Comment From: jhoeller

It's not clear to me whether you are testing this within the managed Spring application context lifecycle or outside of it. Generally, we apply shutdownNow and explicit cancelling which does trigger InterruptedException in remaining tasks. Only if you explicitly switch on setWaitForTasksToCompleteOnShutdown(true) as you do in your quoted setup, that is bypassed to avoid interruption.

Note that we apply setAwaitTerminationSeconds(1) after the executor shutdown call, not before, so it is by default the maximum time to wait after interruption until all tasks have actually completed (following ExecutorService.awaitTermination semantics). That's why interruption happens immediately when the final shutdown stage has been reached.

As a side note: More sophisticated multi-phased graceful shutdown behavior comes through early stopping of tasks and early executor shutdown in the managed Spring lifecycle. Task implementations can explicitly participate in this if needed, implementing the Lifecycle interface for start and stop signals, but primarily this happens through early stopping of the executor itself.

With that intended termination semantic in mind, could you try to make it work through removing setWaitForTasksToCompleteOnShutdown(true)? That would give you immediate interruption of remaining tasks on shutdown, with an await-termination time after that round of interruption.