When a Spring Boot application defines its own ScheduledExecutorService bean (e.g. via Executors.newSingleThreadScheduledExecutor()
), the application hangs indefinitely after receiving a SIGTERM signal if there are active @Scheduled(cron = …)
tasks.
This only occurs starting with Java 19, because ExecutorService now implements AutoCloseable.
Spring detects the bean as AutoCloseable and calls close() during context shutdown. However, if the executor is used for @Scheduled
cron jobs, it waits for the recurring tasks to finish — which never happens — resulting in a stuck shutdown.
Steps to Reproduce: * Use Java 20 or higher * Create a Spring Boot app with:
@SpringBootApplication
@EnableScheduling
public class DemoApp implements SchedulingConfigurer {
public static void main(String[] args) {
SpringApplication.run(DemoApp.class, args);
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean
public Executor taskExecutor() {
return Executors.newSingleThreadScheduledExecutor();
}
@Scheduled(cron = "* 0 * * * *")
public void runJob() {
System.out.println("Running job...");
}
}
- Run the application
- Send a SIGTERM (e.g.
kill -15 <pid>
) - Observe that the application does not exit
Expected behavior: Spring should shut down cleanly, cancelling or stopping recurring tasks using the executor.
Actual behavior: Shutdown hangs indefinitely, because the executor’s close() waits for the cron task to complete, but the task is recurring and never terminates.
Potential workarounds:
* Use Spring's default TaskScheduler
, it's handled properly by calling shutdownNow()
.
* Use ThreadPoolTaskScheduler
or ThreadPoolTaskExecutor
instead of directly creating an Executor with Executors.newSingleThreadScheduledExecutor()
.
Suggestion:
Maybe a better approach would be always handling the custom ScheduledExecutorService
the same way as localExecutor
in ScheduledTaskRegistrar
? But in this case, we don't cover custom TaskScheduler
case... Any idea or suggestions are welcome 🙏
Comment From: jhoeller
ScheduledAnnotationBeanPostProcessor
should cancel the recurring tasks on shutdown, in addition to the ExecutorService
itself closing down. Any indication why this is not happening in your scenario?
In any case, we need to make sure that the out-of-the-box experience with such a custom ExecutorService
does not lead to an indefinite hanging on shutdown.