Shutdown Hooks是一种特殊的结构,它允许开发人员插入JVM关闭时执行的一段代码
用途
- Application正常退出(所有线程完成时,或在调用
System.exit(0)时),执行特定的业务逻辑或关闭资源等操作 - Application非正常退出(用户按下Ctrl+C、操作系统关闭(kill pid,不带-9)),在退出时执行必要的挽救措施
用法
示例:
public class ShutDownHook {
public static void main(String[] args) {
Runtime.getRuntime()
.addShutdownHook(new Thread(() -> System.out.println("Shutdown Hook is running !")));
System.out.println("Application Terminating ...");
}
}
输出:
Application Terminating ...
Shutdown Hook is running !
可以看到 Shutdown Hook is running ! 输出在 Application Terminating ... 之后
陷阱
1.在某些情况下,可能无法执行Shutdown Hook
如果JVM由于某些内部错误而崩溃,则它可能崩溃而没有机会执行一条指令。另外,如果操作系统发出SIGKILL信号(在Unix/Linux中为kill -9)或TerminateProcess(Windows),则要求应用程序立即终止而无需甚至在等待任何清理活动。除上述内容外,还可以通过调用 Runtime.halt() 方法来终止JVM,而不允许运行Shutdown Hook。
2.启动后,可以在完成之前强行关闭Shutdown Hook
在诸如操作系统关闭之类的情况下,有可能在Shutdown Hook完成之前将其终止。在这种情况下,一旦给出SIGTERM,O/S将等待进程终止指定的时间。如果该过程未在此期限内终止,则操作系统将通过发出SIGTERM(或Windows中的对应程序)来强制终止该过程。因此,当关闭Shutdown Hook执行到一半时,可能会发生这种情况。
因此,建议确保Shutdown Hook的谨慎书写,以确保它们快速完成,并且不会引起死锁等情况。另外,JavaDoc特别提到不应在Shutdown Hook中执行长时间计算或等待用户I/O操作。
3.可以有多个Shutdown Hook,但是不能保证它们的执行顺序
可以注册多个Shutdown Hook,但是JVM无法保证其执行顺序(shutdownHooks存放在IdentityHashMap中),JVM可以按任意顺序执行关闭Shutdown Hook,也可能会同时执行Shutdown Hooks。
public void addShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
ApplicationShutdownHooks.add(hook);
}
class ApplicationShutdownHooks {
/* The set of registered hooks */
private static IdentityHashMap<Thread, Thread> hooks;
static synchronized void add(Thread hook) {
if(hooks == null)
throw new IllegalStateException("Shutdown in progress");
if (hook.isAlive())
throw new IllegalArgumentException("Hook already running");
if (hooks.containsKey(hook))
throw new IllegalArgumentException("Hook previously registered");
hooks.put(hook, hook);
}
}
4.关闭程序开始后,无法注册/取消注册Shutdown Hook
一旦关闭程序是由JVM发起的,将不在允许添加或删除任何现有的Shutdown Hook,否则抛出IllegalStateException异常。
5.关闭程序开始后,只能由Runtime.halt()停止
一旦关闭程序开始,只有 Runtime.halt()(强制终止JVM)可以停止执行关闭序列(除了诸如SIGKILL之类的外部影响之外)。这意味着在Shutdown Hook中调用 System.exit() 将不起作用。实际上,如果在Shutdown Hook中调用 System.exit(),VM可能会卡住,我们可能不得不强制终止该过程。
6.使用Shutdown Hook需要安全权限
如果我们使用的是Java Security Manager,则执行添加/删除Shutdown Hook的代码在运行时需要具有shutdownHooks权限。如果我们在安全的环境中未经许可调用此方法,则将导致SecurityException。