今天偶然看到 java.lang.Runtime
类的一个方法 public void addShutdownHook(Thread hook)
。
它的 javadoc
是这么写的:
Registers a new virtual-machine shutdown hook.
The Java virtual machine shuts down in response to two kinds of events:
- The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or
- The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.
意思就是说可以给 JVM
注册一个钩子,这个钩子将在虚拟机关闭的执行。当然这个 关闭 是有条件的。
写个例子
1 | public class ShutdownHookTest { |
使用 javac
编译后运行,得到的输出结果是:
1 | Main thread start |
结果符合文章刚开始引文的第一个情况,当程序的正常退出时会执行注册的钩子。也就是说,在程序主线程(实际上是所有的 demon线程
)结束后,会启动执行钩子线程。
程序非执行完成推出的例子
稍微改一下上面的代码:
1 | public class ShutdownHookTest { |
同样使用 javac
编译后运行。不同的是,在程序启动后按 ctrl + c
停止程序,将得到下面的输出:
1 | Main thread start |
这个结果也符合文章开始引文的第二个情况。当虚拟机用为用户输入 ^C
时,虚拟机会调用已注册的钩子。
另外因为也提到了当用户注销和系统关闭时也会调用已注册的钩子,这里就不做验证了。
钩子不能执行的情况
同样是上面的例子。程序在启动后,打开任务管理器(Windows),找到对应的进程并结束。这时控制台的输出为:
1 | Main thread start |
从输出可以看到,钩子并没有执行。
这就说明,在虚拟机中止 (注意:这里的中止不同于退出或停止,是指异常的break) 的情况下,钩子不会被调用执行。
javadoc
也给出了这种情况的说明:
在极少数虚拟机被外部中止的情况下,例如:
- 在 Unix 上使用 SIGKILL 信号
- 在 Windows 上使用 TerminateProcess 调用
- 执行本地方法出错
也就是说,虚拟机在没有干净地关闭的情况下停止运行,虚拟机则不能保证是否正确的运行关机钩子。
移除钩子
移除钩子使用方法 public boolean removeShutdownHook(Thread hook)
即可。
使用场景
看到这个特性,第一个想到的场景就是。程序在关闭时可以记录一个日志或发送一个通知。
当然,这个基于这个特性,可以定制出来很多使用场景。
但是,鉴于这个钩子的执行时机,就有很多需要注意的地方:
- 关机钩子(shutdown hook)必须是一个已初始化但未启动的线程
- 如果注册了多个钩子,则不能保证这些钩子的执行顺序,他们是同时开始的
- 当虚拟机关机序列开始,则无法在注册或取消钩子
- 对于钩子的线程的编写,应该是线程安全的,并尽可能地避免出现死锁
- 钩子的执行时间不应过长,也不应该添加任何用户交互功能
- 这时因为当用户注销或者关机时,底层操作系统可能只允许有限的固定时间来关闭和退出虚拟机
最后
文章就写到这里。
这篇文章没啥深入的探究,只是突然发现了一个之前未曾注意到的功能,简单的做一下记录和测试。
大家如果要在生产环境中使用,要是场景复杂还是慎重些,上一章节的那些注意事项都需要考虑考虑。
欢迎关注我的公众号 须弥零一,跟我一起学习IT知识。
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !