想象一下,若是你如今恰好在 word 上写需求文档,电脑忽然重启。等待开机完成,你可能会发现写了一个小时文档没有保存,就这么没了。。。html
一个正在运行 Java 应用若是忽然将其中止,影响不止数据丢失,还会形成其余影响。好比:java
因此在关闭服务以前,咱们须要先作好善后工做,好比保存数据,清理资源,下线服务,而后才退出应用。这种有计划平滑的关闭应用相对直接中止应用,就显得很是『优雅』。数据库
ps: 仔细品味,优雅停机这个词真好~
Java 语言提供一种 ShutdownHook(钩子)进制,当 JVM 接受到系统的关闭通知以后,调用 ShutdownHook 内的方法,用以完成清理操做,从而平滑的退出应用。缓存
ShutdownHook代码以下:并发
Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("关闭应用,释放资源"); }));
Runtime.getRuntime().addShutdownHook(Thread)
须要传入一个线程对象,后续动做将会在该异步线程内完成。除了主动关闭应用(使用 kill -15 指令),如下场景也将会触发 ShutdownHook :框架
System#exit
方法Ctrl+C
(非后台运行)目前不少开源框架都是基于这个机制实现优雅停机,好比 Dubbo,Spring 等。异步
ShutdownHook 代码实现起来相对简单,可是咱们仍是须要当心下面这些坑。ide
Runtime.getRuntime().addShutdownHook(Thread)
能够被屡次调用idea
咱们能够屡次调用 Runtime.getRuntime().addShutdownHook(Thread)
方法,从而增长多个。可是须要注意的是,多个 ShutdownHook 之间并没有任何顺序,Java 并不会按照加入顺序执行,反而将会并发执行。spa
因此尽可能在一个 ShutdownHook 完成全部操做。
ShutdownHook 须要尽快执行结束
不要在 ShutdownHook 执行须要被阻塞代码,如 I/0 读写,这样就会致使应用短期不能被关闭。
Runtime.getRuntime().addShutdownHook(new Thread(() -> { while (true){ System.out.println("关闭应用,释放资源"); } }));
上面代码中,咱们使用 while(true)
模拟长时间阻塞这种极端状况,关闭该应用时,应用将会一直阻塞在 while
代码中,致使应用没办法被关闭。
除了阻塞以外,还须要当心其余会让线程阻塞的行为,好比死锁。
为了不 ShutdownHook 线程被长时间阻塞,咱们能够引入超时进制。若是等待必定时间以后,ShutdownHook 还未完成,由脚本直接调用 kill -9 强制退出或者 ShutdownHook 代码中引入超时进制。
文章首发于studyidea.cn
欢迎关注个人公众号:程序通事,得到平常干货推送。若是您对个人专题内容感兴趣,也能够关注个人博客: studyidea.cn