温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

ShutdownHook如何让应用优雅的停止

发布时间:2022-01-04 18:10:26 阅读:191 作者:柒染 栏目:大数据
开发者测试专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

今天就跟大家聊聊有关ShutdownHook如何让应用优雅的停止,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

对于应用,停止的时候一般都会把环境和数据恢复到运行前的样子,和单元测试时tearDown要做的效果基本类似。而为了实现停止时恢复现场,英文中有个特定的描述:

shutdown gracefully

在Java中,为了实现gracefully shutdown,有一个特定的接口可供使用,就是我们今天要提到的shutdown hook。它与回调函数功能类似,文档中对其描述如下:

关闭钩子 只是一个已初始化但尚未启动的线程。虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,并让它们同时运行。运行完所有的钩子后,如果已启用退出终结,那么虚拟机接着会运行所有未调用的终结方法。最后,虚拟机会暂停。注意,关闭序列期间会继续运行守护线程,如果通过调用 exit 方法来发起关闭序列,那么也会继续运行非守护线程。

要为应用添加shutdownHook,需要做的只是这样的下操作:

Runtime.getRuntime().addShutdownHook(new Thread() {
 
    public void run() { /*
       my shutdown code here
    */ }
 });

向addShutdownHook方法传入的Thread,其run方法即为自定义的shutdown时清理逻辑。

JDK内部,是通过一个Map来保存所有添加的ShutdownHook,在被触发时执行。

public void addShutdownHook(Thread hook) {    SecurityManager sm = System.getSecurityManager();    if (sm != null) {        sm.checkPermission(new RuntimePermission("shutdownHooks"));    }    ApplicationShutdownHooks.add(hook);
 
 
static synchronized void add(Thread hook) {     hooks.put(hook, hook);}
 
 
private static IdentityHashMap<ThreadThread> hooks;
 
hooks = new IdentityHashMap<>();

那这个shutdownHook一般是在什么时候会被调用呢?

我们看JDK的文档中描述,对于两类事件Java虚拟机会退出:

Java 虚拟机会为了响应以下两类事件而关闭

  • 程序正常退出,这发生在最后的非守护线程退出时,或者在调用 exit(等同于 System.exit)方法时。或者,

  • 为响应用户中断而终止 虚拟机,如键入 ^C;或发生系统事件,比如用户注销或系统关闭。 

在Java虚拟机退出的时候,这些设置的shutdownHook会被并行的调用。但需要注意的是,对于非正常方式退出Java虚拟机,例如杀进程,系统断电等,这些情况下,shutdownHook不会被执行。

我们来看,在Tomcat内部,是如何使用ShutdownHook的。

Catalina类内部,在服务器内部各个组件启动完毕后,有这样一段代码

 
// Register shutdown hookif (useShutdownHook) {    if (shutdownHook == null) {        shutdownHook = new CatalinaShutdownHook();    }    Runtime.getRuntime().addShutdownHook(shutdownHook);
 
}

和我们在本文开头看到的一样,他注册了一个ShutdownHook。这个hook的内容如下:

 
/** * Shutdown hook which will perform a clean shutdown of Catalina if needed. */protected class CatalinaShutdownHook extends Thread {    public void run() {        try {            if (getServer() != null) {                Catalina.this.stop();            }        } catch (Throwable ex) {        } finally {        }    }}

看就是在shutdown的时候通过调用应用服务器的stop方法,来shutdown gracefully。

而我们一般停止Tomcat会通过调用shutdown脚本的方式进行,这个时候,脚本实质上去执行了应用服务器的stop方法来进行停止,而不是被shutdownHook来反调过来的。

因此,在读源码时,你会发现代码中有这样的内容:

/** * Stop an existing server instance. */public void stop() {    try {        // Remove the ShutdownHook first so that server.stop()        // doesn't get invoked twice        if (useShutdownHook) {         Runtime.getRuntime().removeShutdownHook(shutdownHook);      } catch (Throwable t) {        ExceptionUtils.handleThrowable(t);}

我们看到,在停止Server的时候,会先执行removeShutdownHook的操作,注释里写的明白,是为了防止server和stop被执行两次。

哪种情况下会被执行两次呢?

当使用catalina的shutdown脚本停止Server时,这个方法并不是从shutdownHook调用过来,此时,shutdownHook还没有执行,所以在JVM退出的时候,shutdownHook会被触发。如果此处还没去remove掉的话,就还会调用过来。这一点在我们自己开发应用时需要注意借鉴一下。

看到这里,希望你不要说然并卵。

当然,如果你已经脱口而出,那...

我找了例子证明,你已经在不知不觉中使用了shutdownHook,例如你在输出日志的时候,以下代码是JDK的LogManager中的一部分,我们看到,其构造方法中直接会添加一个名为Cleaner的shutdownHook。

private LogManager(Void checked) {    // Add a shutdown hook to close the global handlers.    try {        Runtime.getRuntime().addShutdownHook(new Cleaner());    } catch (IllegalStateException e) {        // If the VM is already shutting down,        // We do not need to register shutdownHook.    }}
 

public void run() {

// This is to ensure the LogManager.<clinit> is completed    // before synchronized block. Otherwise deadlocks are possible.    LogManager mgr = manager;    // Do a reset to close all active handlers.    reset(); }

我们自己添加shutdownHook的时候,需要注意的是:

在内部不要写耗时的操作,也不要写容易引起死锁的操作。多个ShutdownHook之间如果存在资源竞争而死锁,那应用就停止不了了。

看完上述内容,你们对ShutdownHook如何让应用优雅的停止有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:https://my.oschina.net/u/4585957/blog/4583300

AI

开发者交流群×