如何在Java服务收到SIGTERM信号时实现优雅停机并退出循环?
Spring Boot 优雅停机实现(响应SIGTERM信号)
针对你的需求,结合Java 17、Spring Boot 3.3.1的环境,可以通过线程可见性标志位+Spring容器关闭事件监听的方式实现:收到SIGTERM信号后,允许当前任务执行完成,再退出循环并执行后续收尾逻辑。
具体实现代码
@Service @RequiredArgsConstructor public class AsyncService { // 用volatile保证多线程间的可见性,避免指令重排导致的状态不一致 private volatile boolean shouldShutdown = false; @Async public void run() { while (!shouldShutdown) { // 执行当前任务逻辑 processCurrentTask(); } // 循环退出后执行停机收尾处理 postShutdownProcess(); } private void processCurrentTask() { // 替换为你的实际任务逻辑 try { System.out.println("正在处理当前任务..."); // 模拟任务执行耗时 Thread.sleep(1000); } catch (InterruptedException e) { // 重置线程中断状态,避免影响后续逻辑执行 Thread.currentThread().interrupt(); // 收到中断时直接触发停机逻辑 shouldShutdown = true; } } private void postShutdownProcess() { // 替换为你的停机后收尾逻辑 System.out.println("执行停机后的后续处理..."); } // 监听Spring容器关闭事件(对应SIGTERM信号触发的容器 shutdown) @EventListener(ContextClosedEvent.class) public void onContextClosed() { shouldShutdown = true; System.out.println("收到停机信号,将在当前任务完成后退出循环"); } }
关键配置说明
在application.properties或application.yml中开启Spring Boot优雅停机并设置超时时间,确保容器等待异步任务完成收尾:
# 开启优雅停机模式 spring.shutdown.grace-period=30s # 设置每个关闭阶段的超时时间 spring.lifecycle.timeout-per-shutdown-phase=30s
核心原理说明
- volatile标志位:
shouldShutdown变量用volatile修饰,保证主线程(监听容器关闭事件的线程)修改后,异步线程能立刻感知到状态变化,避免循环无法退出的问题。 - 容器关闭事件监听:
@EventListener(ContextClosedEvent.class)会在Spring容器收到SIGTERM信号时触发,此时设置停机标志位,通知异步线程退出循环。 - 中断处理:任务执行中若收到中断信号,重置中断状态并触发停机逻辑,保证异常场景下也能优雅退出。
- 优雅停机配置:通过Spring Boot的配置参数,确保容器不会强制杀死线程,而是等待指定时间让异步任务完成收尾工作。
测试方式
启动应用后,通过kill <进程ID>命令发送SIGTERM信号(默认kill命令发送的就是该信号),观察控制台日志:会先打印"收到停机信号...",待当前任务执行完成后,退出循环并执行后续收尾逻辑。
内容的提问来源于stack exchange,提问作者Justin Xu
相关产品推荐
相关产品推荐

