java线程池shutdownnow后怎么再启动_哦,这就是java的优雅停机?(实现及原理)...

作者:等你归去来原文:https://www.cnblogs.com/yougewe/p/9881874.html

优雅停机? 这个名词我是服的,如果抛开专业不谈,多好的名词啊!

其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程、释放连接资源等。

再比如,就是不会让调用方的请求处理了一增,一下就中断了。而处理完本次后,再停止服务。

Java语言中,我们可以通过Runtime.getRuntime().addShutdownHook()方法来注册钩子,以保证程序平滑退出。(其他语言也类似)

来个栗子:

14ad7697514f8f8f9046ee24695c7390.png
55b9735598b1b1c803601db0ec403b74.png
e1a3389f946bcfa8e3f9a29915b8f80e.png

运行结果如下:

ffb0a3be77b7f55bf21253f74dbe28c8.png

很明显,确实是优雅了,虽然最后收到了一关闭信号,但是仍然保证了任务的处理完成。很棒吧!

那么,在实际应用中是如何体现优雅停机呢?

kill -15 pid 

通过该命令发送一个关闭信号给到jvm, 然后就开始执行 Shutdown Hook 了,你可以做很多:

  • 关闭 socket 链接
  • 清理临时文件
  • 发送消息通知给订阅方,告知自己下线
  • 将自己将要被销毁的消息通知给子进程
  • 各种资源的释放

而在平时工作中,我们不乏看到很多运维同学,是这么干的:

kill -9 pid

如果这么干的话,jvm也无法了,kill -9 相当于一次系统宕机,系统断电。这会给应用杀了个措手不及,没有留给应用任何反应的机会。

所以,无论如何是优雅不起来了。

要优雅,是代码和运维的结合!

其中,线程池的关闭方式为:

executorService.shutdown();executorService.awaitTermination(1500, TimeUnit.SECONDS);

ThreadPoolExecutor 在 shutdown 之后会变成 SHUTDOWN 状态,无法接受新的任务,随后等待正在执行的任务执行完成。意味着,shutdown 只是发出一个命令,至于有没有关闭还是得看线程自己。

ThreadPoolExecutor 对于 shutdownNow 的处理则不太一样,方法执行之后变成 STOP 状态,并对执行中的线程调用 Thread.interrupt() 方法(但如果线程未处理中断,则不会有任何事发生),所以并不代表“立刻关闭”。

shutdown() :启动顺序关闭,其中执行先前提交的任务,但不接受新任务。如果已经关闭,则调用没有附加效果。此方法不等待先前提交的任务完成执行。

shutdownNow():尝试停止所有正在执行的任务,停止等待任务的处理,并返回正在等待执行的任务的列表。当从此方法返回时,这些任务将从任务队列中耗尽(删除)。此方法不等待主动执行的任务终止。

executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)); 控制等待的时间,防止任务无限期的运行(前面已经强调过了,即使是 shutdownNow 也不能保证线程一定停止运行)。

注意:

  • 虚拟机会对多个shutdownhook以未知的顺序调用,都执行完后再退出。
  • 如果接收到 kill -15 pid 命令时,执行阻塞操作,可以做到等待任务执行完成之后再关闭 JVM。同时,也解释了一些应用执行 kill -15 pid 无法退出的问题,如:中断被阻塞了,或者hook运行了死循环代码。

实现原理:

Runtime.getRuntime().addShutdownHook(hook); // 添加钩子,开启优雅之路

具体流程如下:

9befff460f3686834f5374fdb78b5998.png
49277b68883a51cc9406586710008db7.png
cec387fc242a04e993f0ef7591683cb2.png

到现在为止,我们已经知道关闭钩子是如何执行的,但是,还不是知道,该钩子是何时触发?

c330ddf275269afa5647996a18213cba.png
6297b5077103c17e5581f96d262d4f28.png

如此,整个关闭流程完美了。

简化为:

  • 1. 注册流程(应用主动调用)
Runtime.addShutdownHook -> ApplicationShutdownHooks.add()/static -> java.lang.Shutdown.add()/shutdown()
  • 2. 执行流程(jvm自动调用)
java.lang.Shutdown.shutdown()->sequence()->runHooks() -> ApplicationShutdownHooks.runHooks() -> hooks