FunTester JVM 关闭前做点什么

FunTester · 2023年08月17日 · 2538 次阅读

今天学到了一个非常有趣的 API:java.lang.Runtime#addShutdownHook,顾名思义,就是 JVM shutdown 的钩子,当 JVM 关闭时触发的。addShutdownHook 方法是 java.lang.Runtime 类提供的一个方法,用于注册在 Java 虚拟机即将关闭时执行的代码块(也称为 “钩子” 或 “hook”)。这个代码块会在程序终止之前被执行,无论是正常终止还是由于异常终止。

ShutdownHook 介绍

具体来说,addShutdownHook 方法允许你向 Java 虚拟机注册一个Thread线程,当虚拟机即将关闭时,这个线程会被启动并执行一些清理或其他的操作。

这个方法在以下场景中特别有用:

  1. 资源释放和清理: 当程序退出时,可能需要确保释放资源(如文件、网络连接等)以及做一些清理工作,这样可以避免资源泄漏。
  2. 状态保存: 如果你希望在程序关闭时保存一些状态或数据,可以使用 addShutdownHook 来执行保存操作。
  3. 日志记录: 在程序关闭时记录一些日志,以便后续分析和排查问题。

简单看了一下文档,大概常见 3 中常见的终止场景都是支持的:

  1. JVM 异常终止
  2. 用户主动关闭 JVM(ctrl + C、IDE 终止功能)
  3. 主动调用System.exit()

值得注意的是,addShutdownHook 注册的代码块在运行时是没有明确定义的执行顺序的,这意味着不能保证它们会按照特定的顺序执行。此外,因为钩子在退出时执行,所以应该避免执行耗时很长的操作,以免影响程序的退出速度。

使用演示

在日常的工作中,经常遇到一个造数据或者清洗大量数据。比如我们有 100w 个用户,但是符合条件的只有 10w 个,需要获取用户信息(请求接口)做一个过滤。针对这种场景我常用思路就是开多线程跑,只要有符合条件的用户,就把用户信息存入到一个线程安全的集合,通常是java.util.Vector。最后将这个集合存到文件中。具体代码这里就不演示了,可以参考之前的造数据的文章。

但是在使用过程中难免遇到异常情况:服务不稳定网络异常账户异常缺少校验等等,都会导致运行中断,但是这时候已经有部分用户筛选过了。

这个时候我们添加一个java.lang.Runtime#addShutdownHook钩子,当系统异常中断或者人为手动中断时候,可以将java.util.Vector中保存的有用数据存下来。

演示代码如下:

public static void main(String[] args) {
    Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println(3)));
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(){
        @Override
        public void run() {
            System.out.println(32);
        }
    }));
    System.exit(8);
}

如果使用 Groovy 的closure封装一层,代码如下:

/**
 * 添加ShutdownHook
 * @param closure
 * @return
 */
static def addHook(Closure closure) {
    Runtime.getRuntime().addShutdownHook(new Thread(closure))
}

然后我突然发现 Groovy 原声的 API 就有这个功能:

/**
 * Allows the usage of addShutdownHook without getting the runtime first.
 *
 * @param self    the object the method is called on (ignored)
 * @param closure the shutdown hook action
 * @since 1.5.0
 */
public static void addShutdownHook(Object self, Closure closure) {
    Runtime.getRuntime().addShutdownHook(new Thread(closure));
}

源码发掘

当我再次翻看java.lang.Runtime#addShutdownHook源码时候发现这个钩子是可以多个的,当我们添加带多余 1 个钩子且 JVM 关闭时,钩子的执行顺序是不是添加顺序。

Java 文档如下:

When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently.

当虚拟机开始关闭时,会运行所有注册过的 hook 线程,顺序是不确定的,并发运行。so,我们可以注册多个 hook。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册