Java应用正确地使用shutdownhook
在线上Java程序中经常遇到进程程挂掉,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码。JDK在1.3之后提供了Java Runtime.addShutdownHook(Thread hook)方法,可以注册一个JVM关闭的钩子,这个钩子可以在以下几种场景被调用:
- 程序正常退出
- 使用System.exit()
- 终端使用Ctrl+C触发的中断
- 系统关闭
- 使用Kill pid命令干掉进程
注:在使用kill -9 pid 时JVM注册的钩子不会被调用。
在JDK中方法的声明:
1 | public void addShutdownHook(Thread hook) |
标准中断信号
在Linux信号机制中,存在多种进程中断信号(Linux信号列表 )。其中比较典型的有 SIGNKILL(9) 和 SIGNTERM(15).
SIGNKILL(9) 和 SIGNTERM(15) 的区别在于:
SIGNKILL(9) 的效果是立即杀死进程. 该信号不能被阻塞, 处理和忽略。
SIGNTERM(15) 的效果是正常退出进程,退出前可以被阻塞或回调处理。并且它是Linux缺省的程序中断信号。
测试用例
1 | import java.util.Timer; |
shutdownhook的源码分析
ApplicationShutdownHooks
Runtime类中对ShutdownHook的操作都是通过工具类ApplicationShutdownHooks来实现的。
1 | class ApplicationShutdownHooks { |
Shutdown
下面来看下Shutdown的源码
1 | class Shutdown { |
shutdown的字段state的状态机如下图所示:
graph LR id0((begin))-->id1(running) id1-->|"Runtime.exit()"|id2(HOOKS) id1-->|"shutdown()"|id2 id2-->|"sequence()"|id3(FINALIZERS) id3-->id4((end))
delete-on-exit file list
java.io.File的deleteOnExit()方法可以在JVM退出时,删除文件。主要的实现就是DeleteOnExitHook.
1 | class DeleteOnExitHook { |