shutdownHook
是一种特殊的结构,它容许开发人员插入JVM关闭时执行
的一段代码。这种状况在咱们须要作特殊清理操做
的状况下颇有用html
<!-- more -->java
在Jboss
,Jetty
等容器中均可以看到shutdownHook
的身影,例如在服务优雅下线一文中的spring-boot-starter-actuator
就会触发shutdownHook
...git
Application
正常退出,在退出时执行特定的业务逻辑,或者关闭资源等操做。正常退出spring
public class ShutdownHook { public static void main(String[] args) throws InterruptedException { Runtime.getRuntime().addShutdownHook(new Thread(() -> { try (FileWriter fw = new FileWriter("hook.log")) { // 假设记录日志/或者发送消息 fw.write("完成销毁操做,回收内存! " + (new Date()).toString()); System.out.println("退出程序..."); } catch (IOException e) { e.printStackTrace(); } })); IntStream.range(0, 10).forEach(i -> { try { System.out.println("正在工做..."); Thread.sleep(2_000L); } catch (InterruptedException e) { e.printStackTrace(); } }); } }
当咱们运行上面的代码时,会看到在完成main方法的执行时JVM调用shutdownHook
。api
正在工做... ... 正在工做... 退出程序...
kill pid方式安全
虽然编写一个shutdownHook
很简单,可是须要了解shutdownHook
后面的内部部件才能正确使用。所以,在后续中,将探讨shutdownHook
设计背后的一些陷阱
。微信
应用程序没法保证shutdownHook
老是运行的oracle
如JVM因为某些内部错误而崩溃,或(Unix / Linux中的kill -9)或TerminateProcess(Windows)),那么应用程序须要当即终止而不会甚至等待任何清理活动。除了上述以外,还能够经过调用Runime.halt()
方法来终止JVM,而阻止shutdownHook
运行。spring-boot
shutdownHook
能够在完成前强行中止spa
虽然shutdownHook
开始执行,可是在操做系统关闭的状况下,任然能够在完成以前终止它。在这种状况下,一旦SIGTERM
被提供,O/S
等待一个进程终止指定的时间。若是进程在该时间限制内没有终止,则O/S
经过发出SIGTERM(或Windows中的对等方)强制终止进程。因此有可能这是在shutdownHook
中途执行时发生的。
所以,建议谨慎地编写shutdownHook
,确保它们快速完成,而且不会形成死锁等状况。另外特别注意的是,不该该执行长时间计算或等待用户I/O操做在钩子。
能够有多个shutdownHook
,但其执行顺序没法保证
public void addShutdownHook(Thread hook) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("shutdownHooks")); } ApplicationShutdownHooks.add(hook); } class ApplicationShutdownHooks { /* The set of registered hooks */ private static IdentityHashMap<Thread, Thread> hooks; 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); } }
经过源码发现,能够注册多个shutdownHook
。可是由于它是存储在IdentityHashMap
中的,JVM并不能保证其执行顺序。可是能够同时执行全部的shutdownHook
关闭顺序开始后,没法注册/取消注册shutdownHook
一旦关闭顺序是由JVM发起的,将不在容许添加或删除任何现有的shutdownHook
,不然抛出IllegalStateException
异常。
关闭顺序开始后,只能由Runtime.halt()
中止
关闭顺序开始后,只能经过Runtime.halt()(强制终止JVM)
,能够中止关闭顺序的执行(外部影响除外,如SIGKILL)。
使用shutdownHook
须要安全权限
若是咱们使用Java Security Managers
,则执行添加/删除shutdownHook
的代码须要在运行时
具备shutdownHooks
权限。不然会致使SecurityException
。
微信公众号:battcn
(欢迎调戏)