关闭方式
正常关闭
- 最后一个普通线程(非守护线程)结束
- 调用了System.exit
- 发送SIGINT信号(相当于不带参数的kill命令)或者键入Ctrl-C
强制关闭
- 调用Runtime.halt
- 发送SIGKILL信号(kill -9 命令)
关闭钩子(Shutdown Hook)
钩子配置方法
通过下面的设置方法可看到,关闭钩子实际为线程
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
}));
触发调用Hook线程流程
正常关闭中,JVM首先调用所有已注册的关闭钩子。JVM并不能保证关闭钩子的调用顺序。在关闭应用程序线程时,如果有线程仍然在运行,那么这些线程接下来将与关闭进程并发进行。
当所有的关闭钩子都执行结束时,如果runFinalizersOnExit为true,那么JVM将运行终结器,然后再停止。JVM并不会停止或中断任何在关闭时仍然运行的应用程序线程。当JVM最终结束时,这些线程将被强行结束。
如果关闭钩子或终结器没有执行完成,那么正常关闭进程挂起并且JVM必须被强行关闭。
–《Java并发编程实战》
上述是书里面的介绍,下面跟着源码对照整个流程:
添加钩子
Runtime:
public void addShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
ApplicationShutdownHooks.add(hook);
}
ApplicationShutdownHooks:
会检查hook线程是否被启动,是否已经添加过
其中看到第一个判断条件是判断hooks是否为空,这是因为当ApplicationShutdownHooks开始启动钩子线程后,会把hooks置为空,也意味着开始启动钩子线程后,就再也无法添加钩子线程
/* Add a new shutdown hook. Checks the shutdown state and the hook itself,
* but does not do any security checks.
*/
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);
}
关闭钩子管理器的初始化
从上面代码可以看到,实际管理关闭钩子的是ApplicationShutdownHooks类,
可以看到ApplicationShutdownHooks其实是属于 用户/应用程序等级(user level) 的关闭钩子管理器(注释),该类初始化的时候,会把自己注册到虚拟机的关闭钩子队列中(Shutdown)(静态初始代码)
初始化:
/*
* Class to track and run user level shutdown hooks registered through
* <tt>{@link Runtime#addShutdownHook Runtime.addShutdownHook}</tt>.
*
* @see java.lang.Runtime#addShutdownHook
* @see java.lang.Runtime#removeShutdownHook
*/
class ApplicationShutdownHooks {
/* The set of registered hooks */
private static IdentityHashMap<Thread, Thread> hooks;
static {
try {
Shutdown.add(1 /* shutdown hook invocation order */,
false /* not registered if shutdown in progress */,
new Runnable() {
public void run() {
runHooks();
}
}
);
hooks = new IdentityHashMap<>();