要触发代码中的 ShutdownHook(关闭钩子),需要让进程优雅退出(而非被强制终止)。Linux 系统中,可通过发送可被进程捕获的终止信号(如 SIGTERM)或让进程主动退出(如执行 System.exit())来实现。以下是具体处理方式:
一、通过系统信号触发(推荐)
发送非强制终止信号(如 SIGTERM,默认的 kill 信号),进程会捕获信号并执行 ShutdownHook 后退出。
1. 常用信号(可触发 ShutdownHook)
| 信号命令 | 信号值 | 含义 | 适用场景 |
|---|---|---|---|
kill <PID> | 15 | 优雅终止(默认信号) | 正常停止进程,触发收尾逻辑 |
kill -15 <PID> | 15 | 同 kill <PID> | 显式指定优雅终止信号 |
kill -2 <PID> | 2 | 中断信号(类似 Ctrl+C) | 模拟用户在终端按下 Ctrl+C |
kill -1 <PID> | 1 | 挂起信号(SIGHUP) | 通常用于让进程重新加载配置(部分程序会触发退出) |
2. 操作示例
假设 Java 进程的 PID 是 12345:
bash
# 方式 1:发送默认的 SIGTERM 信号(推荐)
kill 12345
# 方式 2:显式发送 SIGTERM 信号(与方式 1 等价)
kill -15 12345
# 方式 3:发送 SIGINT 信号(模拟 Ctrl+C)
kill -2 12345
执行后,进程会先执行 ShutdownHook 中的逻辑(如关闭资源、打印日志),然后正常退出。
二、通过进程主动退出触发
在代码中主动调用退出方法(如 System.exit(0)),JVM 会在退出前自动执行所有注册的 ShutdownHook。
1. 代码中主动触发
java
运行
public class ShutdownHookTest {
public static void main(String[] args) {
// 注册 ShutdownHook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("ShutdownHook 执行:释放资源中...");
}));
// 模拟业务逻辑执行一段时间后主动退出
new Thread(() -> {
try {
Thread.sleep(5000); // 5秒后退出
System.out.println("业务执行完毕,准备退出...");
System.exit(0); // 主动调用 exit,触发 ShutdownHook
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
运行后,5 秒后进程会主动执行 System.exit(0),此时 ShutdownHook 会被触发。
2. 外部触发进程主动退出(如通过接口)
在服务中暴露一个 “优雅关闭” 接口,调用接口时执行 System.exit(0),适用于需要远程触发关闭的场景(如微服务的优雅下线):
java
运行
@RestController
public class ShutdownController {
@PostMapping("/shutdown")
public String shutdown() {
// 异步执行退出,避免接口超时
new Thread(() -> {
try {
Thread.sleep(1000); // 预留时间返回响应
System.exit(0); // 触发 ShutdownHook 后退出
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
return "服务将优雅关闭";
}
}
调用 POST /shutdown 接口后,服务会执行 ShutdownHook 并退出。
三、关键注意事项
-
ShutdownHook执行时间有限系统信号(如SIGTERM)触发退出时,操作系统会等待进程一段时间(通常几秒到几十秒),若ShutdownHook执行过久(如卡住),系统可能会强制终止进程。因此,ShutdownHook应只包含轻量的收尾逻辑(如关闭连接、保存缓存,避免复杂操作)。 -
避免在
ShutdownHook中调用System.exit()否则可能导致死锁或无限循环(ShutdownHook调用exit会再次触发ShutdownHook)。 -
多
ShutdownHook的执行顺序不确定JVM 不保证多个注册的ShutdownHook线程的执行顺序,若有依赖关系,需手动协调(如用同步锁)。 -
容器环境中的特殊处理在 Docker、K8s 等容器中,容器停止时默认发送
SIGTERM信号,等待一段时间(如 30 秒)后若进程未退出,会发送SIGKILL强制终止。因此,需确保ShutdownHook在容器的 “优雅停止超时时间” 内执行完毕(可通过terminationGracePeriodSeconds配置超时时间)。
总结
要触发 ShutdownHook,核心是让进程优雅退出:
- 优先使用
kill <PID>(或kill -15 <PID>)发送SIGTERM信号; - 或在代码中主动调用
System.exit(0)(如通过接口触发)。
避免使用 kill -9(SIGKILL),它会直接终止进程,无法触发任何收尾逻辑。
kill <PID>(默认发送 SIGTERM 信号)会触发 JVM 执行所有已注册的 ShutdownHook(关闭钩子)方法,直到所有钩子执行完成(或超时)后,进程才会退出
717

被折叠的 条评论
为什么被折叠?



