源码点击 study 查看
Shutdown hook 是什么
Shutdown hook 是 Jvm 敞开的钩子,是通过 Runtime#addShutdownHook(Thread hook) 办法来实现的,依据 api 是注解可知它就是一系例的已初始化但尚未执行的线程对象。咱们能够通过向 Jvm 注册一个钩子,实现在程序退出时敞开资源、平滑退出的性能。所谓的优雅停机也能够这么搞。
Jvm 敞开的形式
程序只有在失常敞开和异样敞开的状况下才会调用钩子函数坐一些开头的工作,如果是强制敞开的则不会调用,强制敞开间接无磋商终止 jvm 过程,不给 jvm 喘息的机会。
应用敞开钩子的注意事项
- 敞开钩子实质上是一个线程(也称为 Hook 线程),对于一个 JVM 中注册的多个敞开钩子它们将会并发执行,所以 JVM 并不保障它们的执行程序;因为是并发执行的,那么很可能因为代码不当导致呈现竞态条件或死锁等问题,为了防止该问题,强烈建议在一个钩子中执行一系列操作。
- Hook 线程会提早 JVM 的敞开工夫,这就要求在编写钩子过程中必须要尽可能的缩小 Hook 线程的执行工夫,防止 hook 线程中呈现耗时的计算、期待用户 I / O 等等操作。
- 敞开钩子执行过程中可能被强制打断, 比方在操作系统关机时,操作系统会期待过程进行,期待超时,过程仍未进行,操作系统会强制的杀死该过程,在这类状况下,敞开钩子在执行过程中被强制停止。
- 在敞开钩子中,不能执行注册、移除钩子的操作,JVM 将敞开钩子序列初始化结束后,不容许再次增加或者移除曾经存在的钩子,否则 JVM 抛出 IllegalStateException。
- 不能在钩子调用 System.exit(),否则卡住 JVM 的敞开过程,然而能够调用 Runtime.halt()。
- Hook 线程中同样会抛出异样,对于未捕获的异样,线程的默认异样处理器解决该异样,不会影响其余 hook 线程以及 JVM 失常退出
简略例子(具体可看源码)
1. 业务要敞开的资源
public class StudyResource implements AutoCloseable {
@Override
public void close() throws Exception {System.out.println("执行我的项目资源敞开操作");
}
}
2. 自定义钩子
/**
* @author: lixiaoshuang
* @create: 2020-11-25 20:48
**/
public class StudyShtudownHook extends Thread {private static final StudyShtudownHook INSTANCE = new StudyShtudownHook();
/**
* 须要敞开的钩子汇合, 能够将我的项目中的资源敞开操作都放在这里
*/
private final Set<AutoCloseable> autoCloseableHashSet = new HashSet<>();
private StudyShtudownHook() {}
public static StudyShtudownHook getInstance() {return INSTANCE;}
public void registerAutoCloseable(final AutoCloseable autoCloseable) {autoCloseableHashSet.add(autoCloseable);
}
@Override
public void run() {this.closeAll();
}
@SneakyThrows
private void closeAll() {for (AutoCloseable autoCloseable : autoCloseableHashSet) {autoCloseable.close();
}
}
}
- 向 jvm 注册钩子
public class JvmHookDemo {public static void main(String[] args) throws InterruptedException {
// 本人实现的钩子
StudyShtudownHook instance = StudyShtudownHook.getInstance();
// 将须要敞开的资源放到钩子里
StudyResource studyResource = new StudyResource();
instance.registerAutoCloseable(studyResource);
// 向 jvm 注册钩子
Runtime.getRuntime().addShutdownHook(instance);
System.out.println("执行业务逻辑。。。。");
Thread.sleep(5000);
System.out.println("业务逻辑处理完毕。。。。");
}
}
执行这段代码后输入:
执行业务逻辑。。。。
业务逻辑处理完毕。。。。
执行我的项目资源敞开操作