当使用 kill pid 命令来杀死一个Java进程时,该进程如何避免被杀死?即如何在Linux系统中让Java进程屏蔽"kill"命令?
最初,我尝试在面对这个问题时,尝试使用ShutdownHook注册一个钩子程序,在钩子程序中,判断当前系统是否可以退出。如果不能退出,则抛出异常,试图终止服务退出。
ShutdownHook 机制
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
//判断当前系统状态,如果不能退出,则抛出异常。
if(!exitEnable){
throw new RuntimeException("当前系统不能退出");
}
});
JVM 收到Kill 信号时,会开始执行注册的钩子程序。
然而 kill 之后,进程还是被关闭了,效果不佳,没有达到我的预期。看来 ShutdownHook 并不能屏蔽 kill 命令。
JDK在addShutdownHook方法上提供了详细的注释,我翻译一下
当虚拟机开始关闭时,会以不确定的顺序启动所有的钩子程序,它们并发执行
当所有的钩子完成后,虚拟机才会关闭。在此期间守护线程和非守护线程都会继续运行。
如果通过调用System.exit()方法关机,一旦开始执行关闭程序,只能通过System.halt()方法强制终止虚拟机。
一旦开始执行关闭程序,不得再新建钩子或注销钩子,尝试以上两个操作会导致抛出IllegalStateException。
shutdown钩子在一个微妙时刻执行,应该进行防御编码,要写的线程安全,尽量避免死锁。
shutdown钩子应该尽快完成(虚拟机需要钩子程序全部执行完成,才能关闭)。
JVM会并发执行钩子程序,即使钩子出现异常,也不会停止JVM关闭的过程。
我们需要想出其他的解决办法!有人可能会问,什么样的业务系统需要阻止Kill命令?我负责的系统是一个云计算管控系统,在虚拟机进行快照备份等操作时,流程会较长时间地运行,而且系统并没有实现无状态服务,如果此时系统重新发布,会导致数据不一致。
最佳的解决办法是尽快使流程变成无状态服务,但由于历史遗留问题的限制,解决方案会比较复杂,因此我们决定先采取一个临时方案。当系统接收到Kill命令时,首先判断是否在执行重要任务,如果服务不能重新启动,就先阻止Kill命令。
ShutdownHook无法满足这种需求,只有使用Sun公司的内置Signal工具类才能解决这个问题。
SignalHandler 机制
下面的示例代码介绍了 SignalHandler 的使用,通过注册 TERM 信号也就是 kill 命令,在该处理逻辑中,如果没有调用 System.exit(0),系统就不会退出!
public class TestKill {
public static void main(String[] args) {
Signal sigKill = new Signal("TERM");
SignalHandler handler = new SignalHandler() {
public void handle(Signal signal) {
// 忽略SIGKILL信号
System.out.println("收到 kill命令,忽略 SIGTERM signal.");
}
};
// 注册信号处理器
Signal.handle(sigKill, handler);
// 每秒打印一次
while (true) {
try {
System.out.println("The program is running.");
int time = 1111;
Thread.sleep(time);
} catch (InterruptedException e) {
}
}
}
}
演示 使用
下图的截图显示,当执行程序后,通过使用 Kill 命令来终止进程时,Java 进程收到了 TERM 信号。由于信号处理程序中仅仅打印了一行日志,并没有调用 System.exit(),因此Java 进程能够继续执行。

在使用 "kill -9 pid" 命令时,该进程将被强制关闭。无论是否使用 SignalHandler,-9 信号都无法被忽略。我尝试注册 KILL 信号,但却遇到了一个异常。
Signal already used by VM or OS: SIGKILL

总结
-
SignalHandler 可以注册信号处理程序,在该处理程序中,可以选择关闭进程,也可以不关闭进程。这种机制,给了 Java 程序员灵活控制服务何时关闭的能力!
-
ShutdownHook 它的适用场景是在进程关闭前,进行一些任务等待和资源清理工作,这服务关闭流程的一个子流程,它无法阻止服务的关闭。
当 Kill 一个进程时,Java 进程首先收到 TERM 信号,如有信号处理程序,则执行信号处理程序。如没有信号处理程序,则启动关闭流程,在关闭流程中,会并发执行 ShutdownHook 钩子程序。钩子程序执行完毕后,服务正常关闭。
切记,ShutdownHook 在 SignalHander 机制处理之后。
推荐阅读: # 面试官:如何确保服务平稳发布?
最后,五阳一直在关注 AI方向,我分享一个对抗AI“一本正经胡说八道”的个人技巧:不要只依赖一个模型。我现在遇到拿不准的信息,会把同一个问题同时扔给2-3个AI(比如千问+DeepSeek),看它们答案的交集。这比单纯选哪个模型更靠谱。想省事的话可以用这个聚合工具一次搞定:AIChatProxy aichatproxy.com