速读摘要文章源自JAVA秀-https://www.javaxiu.com/1896.html
这个名词我是服的,如果抛开专业不谈,多好的名词啊!Java语言中,我们可以通过Runtime.getRuntime().addShutdownHook()方法来注册钩子,以保证程序平滑退出。启动顺序关闭,其中执行先前提交的任务,但不接受新任务。尝试停止所有正在执行的任务,停止等待任务的处理,并返回正在等待执行的任务的列表。虚拟机会对多个shutdownhook以未知的顺序调用,都执行完后再退出。文章源自JAVA秀-https://www.javaxiu.com/1896.html
原文约 1924 字 | 图片 1 张 | 建议阅读 4 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/1896.html
哦,这就是Java的优雅停机?(实现及原理)
搜云库技术团队 文章源自JAVA秀-https://www.javaxiu.com/1896.html
优雅停机?这个名词我是服的,如果抛开专业不谈,多好的名词啊!文章源自JAVA秀-https://www.javaxiu.com/1896.html
其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程、释放连接资源等。文章源自JAVA秀-https://www.javaxiu.com/1896.html
再比如,就是不会让调用方的请求处理了一增,一下就中断了。而处理完本次后,再停止服务。文章源自JAVA秀-https://www.javaxiu.com/1896.html
Java语言中,我们可以通过Runtime.getRuntime().addShutdownHook()方法来注册钩子,以保证程序平滑退出。(其他语言也类似)文章源自JAVA秀-https://www.javaxiu.com/1896.html
十大城市,程序员工作内推群文章源自JAVA秀-https://www.javaxiu.com/1896.html
高清 3625页大厂面试题 PDF文章源自JAVA秀-https://www.javaxiu.com/1896.html
来个栗子:文章源自JAVA秀-https://www.javaxiu.com/1896.html
publicclassShutdownGraceFullTest {
/**
* 使用线程池处理任务
*/
publicstaticExecutorService executorService = Executors.newCachedThreadPool();
publicstaticvoid main(String[] args) {
//假设有5个线程需要执行任务
for(int i = 0; i < 5; i++){
finalint id = i;
Thread taski = newThread(newRunnable() {
@Override
publicvoid run() {
System.out.println(System.currentTimeMillis() + " : thread_" + id + " start...");
try {
TimeUnit.SECONDS.sleep(id);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " : thread_" + id + " finish!");
}
});
taski.setDaemon(true);
executorService.submit(taski);
}
// 添加一个钩子处理未完任务
Runtime.getRuntime().addShutdownHook(newThread(newRunnable() {
@Override
publicvoid run() {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " No1 shutdown hooking...");
boolean shutdown = true;
try {
executorService.shutdown();
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " shutdown signal got, wait threadPool finish.");
executorService.awaitTermination(1500, TimeUnit.SECONDS);
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " all thread's done.");
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " No1 shutdown done...");
}
}));
// 多个关闭钩子并发执行
Runtime.getRuntime().addShutdownHook(newThread(newRunnable() {
@Override
publicvoid run() {
try {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " No2 shutdown hooking...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " No2 shutdown done...");
}
}));
System.out.println("main method exit...");
// 故意调用jvm退出命令,发送关闭信号,否则正常情况下 jvm 会等待最后一个非守护线程关闭才会退出
System.exit(0);
}
}
运行结果如下:文章源自JAVA秀-https://www.javaxiu.com/1896.html
文章源自JAVA秀-https://www.javaxiu.com/1896.html
很明显,确实是优雅了,虽然最后收到了一关闭信号,但是仍然保证了任务的处理完成。很棒吧!文章源自JAVA秀-https://www.javaxiu.com/1896.html
那么,在实际应用中是如何体现优雅停机呢?文章源自JAVA秀-https://www.javaxiu.com/1896.html
kill -15 pid
通过该命令发送一个关闭信号给到jvm, 然后就开始执行 Shutdown Hook 了,你可以做很多:文章源自JAVA秀-https://www.javaxiu.com/1896.html
1、 关闭 socket 链接文章源自JAVA秀-https://www.javaxiu.com/1896.html
2、 清理临时文件文章源自JAVA秀-https://www.javaxiu.com/1896.html
3、 发送消息通知给订阅方,告知自己下线文章源自JAVA秀-https://www.javaxiu.com/1896.html
4、 将自己将要被销毁的消息通知给子进程文章源自JAVA秀-https://www.javaxiu.com/1896.html
5、 各种资源的释放文章源自JAVA秀-https://www.javaxiu.com/1896.html
...文章源自JAVA秀-https://www.javaxiu.com/1896.html
而在平时工作中,我们不乏看到很多运维同学,是这么干的:文章源自JAVA秀-https://www.javaxiu.com/1896.html
kill -9 pid
如果这么干的话,jvm也无法了,kill -9 相当于一次系统宕机,系统断电。这会给应用杀了个措手不及,没有留给应用任何反应的机会。文章源自JAVA秀-https://www.javaxiu.com/1896.html
所以,无论如何是优雅不起来了。文章源自JAVA秀-https://www.javaxiu.com/1896.html
要优雅,是代码文章源自JAVA秀-https://www.javaxiu.com/1896.html
其中,线程池的关闭方式为:文章源自JAVA秀-https://www.javaxiu.com/1896.html
executorService.shutdown();
executorService.awaitTermination(1500, TimeUnit.SECONDS);
ThreadPoolExecutor 在 shutdown 之后会变成 SHUTDOWN 状态,无法接受新的任务,随后等待正在执行的任务执行完成。意味着,shutdown 只是发出一个命令,至于有没有关闭还是得看线程自己。文章源自JAVA秀-https://www.javaxiu.com/1896.html
ThreadPoolExecutor 对于 shutdownNow 的处理则不太一样,方法执行之后变成 STOP 状态,并对执行中的线程调用 Thread.interrupt() 方法(但如果线程未处理中断,则不会有任何事发生),所以并不代表“立刻关闭”。文章源自JAVA秀-https://www.javaxiu.com/1896.html
shutdown() :启动顺序关闭,其中执行先前提交的任务,但不接受新任务。如果已经关闭,则调用没有附加效果。此方法不等待先前提交的任务完成执行。文章源自JAVA秀-https://www.javaxiu.com/1896.html
shutdownNow():尝试停止所有正在执行的任务,停止等待任务的处理,并返回正在等待执行的任务的列表。当从此方法返回时,这些任务将从任务队列中耗尽(删除)。此方法不等待主动执行的任务终止。文章源自JAVA秀-https://www.javaxiu.com/1896.html
executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)); 控制等待的时间,防止任务无限期的运行(前面已经强调过了,即使是 shutdownNow 也不能保证线程一定停止运行)。文章源自JAVA秀-https://www.javaxiu.com/1896.html
注意:文章源自JAVA秀-https://www.javaxiu.com/1896.html
虚拟机会对多个shutdownhook以未知的顺序调用,都执行完后再退出。文章源自JAVA秀-https://www.javaxiu.com/1896.html
如果接收到 kill -15 pid 命令时,执行阻塞操作,可以做到等待任务执行完成之后再关闭 JVM。同时,也解释了一些应用执行 kill -15 pid 无法退出的问题,如:中断被阻塞了,或者hook运行了死循环代码。文章源自JAVA秀-https://www.javaxiu.com/1896.html
十大城市,程序员工作内推群文章源自JAVA秀-https://www.javaxiu.com/1896.html
高清 3625页大厂面试题 PDF文章源自JAVA秀-https://www.javaxiu.com/1896.html
文章源自JAVA秀-https://www.javaxiu.com/1896.html MyBatis 三种批量插入方式的比较,我推荐第3个!文章源自JAVA秀-https://www.javaxiu.com/1896.html
文章源自JAVA秀-https://www.javaxiu.com/1896.html 面试官问:前后端分离项目,有什么优缺点?文章源自JAVA秀-https://www.javaxiu.com/1896.html
文章源自JAVA秀-https://www.javaxiu.com/1896.html 发现一个,轻量级,分布式日志,标记追踪神器!文章源自JAVA秀-https://www.javaxiu.com/1896.html
推荐,发现一个 GitHub 书籍仓库文章源自JAVA秀-https://www.javaxiu.com/1896.html
文章源自JAVA秀-https://www.javaxiu.com/1896.html
https://github.com/itdevbooks/pdf文章源自JAVA秀-https://www.javaxiu.com/1896.html
这个整理了1000多本 常用 技术书籍PDF,绝大部分核心的高清技术书籍都可以在这里找到!文章源自JAVA秀-https://www.javaxiu.com/1896.html
推荐,GitHub 地址,电脑打开体验更好文章源自JAVA秀-https://www.javaxiu.com/1896.html
阅读原文:一键直达,GitHub 地址文章源自JAVA秀-https://www.javaxiu.com/1896.html
阅读原文文章源自JAVA秀-https://www.javaxiu.com/1896.html

评论