Spring Boot应用执行@Async方法后关闭时挂起问题求助
Spring Boot应用关闭挂起问题(@Async + ApplicationRunner场景)
问题背景
实现ApplicationRunner的Spring Boot应用在关闭时出现挂起:所有带@Async注解的任务已执行完成,但最后一次System.out.println()调用后,应用仍需延迟一段时间才会终止。该问题在Linux Mint(Open-JDK 17.0.7)和树莓派环境下均可复现,存在完整演示项目。
解决方案
1. 自定义异步线程池并配置优雅关闭
Spring Boot默认的@Async线程池未配置优雅关闭策略,导致应用关闭时需等待线程超时退出。自定义线程池并设置任务等待与超时参数:
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(4); executor.setMaxPoolSize(8); executor.setQueueCapacity(10); executor.setThreadNamePrefix("Async-Worker-"); // 关闭时等待所有已提交任务完成 executor.setWaitForTasksToCompleteOnShutdown(true); // 设置最大等待超时时间(按需调整,示例为10秒) executor.setAwaitTerminationSeconds(10); executor.initialize(); return executor; } }
2. 在ApplicationRunner中手动等待异步任务完成
若不想修改线程池,可在ApplicationRunner中跟踪所有异步任务的Future对象,等待全部完成后再结束流程:
@Component public class MainComponent implements ApplicationRunner { private final JobsService jobsService; private final List<Future<?>> asyncTasks = new ArrayList<>(); public MainComponent(JobsService jobsService) { this.jobsService = jobsService; } @Override public void run(ApplicationArguments args) throws Exception { // 提交异步任务并保存Future引用 asyncTasks.add(jobsService.asyncJob1()); asyncTasks.add(jobsService.asyncJob2()); // 等待所有任务执行完成 for (Future<?> task : asyncTasks) { task.get(); } System.out.println("所有任务执行完毕,应用即将关闭"); } }
3. 排查异步方法中的未释放资源
检查@Async注解的方法是否持有未关闭的资源(如数据库连接、文件IO流、网络连接等),此类资源可能导致线程无法正常终止,进而引发应用挂起。
原因解析
Spring Boot关闭时会触发上下文销毁流程:
- 默认
@Async使用的SimpleAsyncTaskExecutor会创建非守护线程,JVM需等待所有非守护线程终止才会退出; - 即使任务执行完成,空闲线程仍需等待超时才会销毁,导致应用出现挂起延迟;
- 自定义线程池配置优雅关闭后,Spring会在上下文销毁时等待线程池内任务完成,并在超时后强制终止线程,避免长时间挂起。
内容的提问来源于stack exchange,提问作者D-Dᴙum
相关产品推荐
相关产品推荐

