Skip to main content
 首页 » 编程设计

给JVM应用注册关闭钩子函数

2022年07月19日122虾米姐

启动一个服务相对容易,但有时我们需要能控制并优雅地关闭服务。本文介绍几种不同方式终止JVM应用,然后使用JAVA API管理JVM关闭回调。

关闭JVM

有两种放啊是关闭JVM:

  1. 控制进程
  2. 意外方式

下面几种情况也属于控制进程关闭JVM:

  • 最后的非精灵现场终止,例如当主线程结束时,JVM开始关闭进程。
  • 从操作系统发送中断信号,例如,按CTRL+C或注销操作系统
  • 通过Java代码调用 System.exit()

当我们希望JVM优雅地关闭,但有时JVM可能被异常方式关闭:

  • 从OS发送kill信号,例如:kill -9 <jvm_pid>
  • 从JAVA 代码调用 Runtime.getRuntime().halt()
  • OS异常死机,例如,断电或OS死机

关闭JVM的回调钩子

JVM支持在完成关闭JVM之前注册函数,这些函数是释放资源或其他任务管理的好地方。在JVM术语中,这些函数被称为关闭钩子。

关闭钩子其实是初始化但未启动的线程。当JVM开始其关闭进程时,它将以未指定的顺序启动所有注册的钩子。在运行所有钩子函数之后,JVM将停止。

注册钩子函数

注册钩子函数,可以使用 Runtime.getRuntime().addShutdownHook() 方法:

‘’’

Thread printingHook = new Thread(() -> System.out.println(“In the middle of a shutdown”));
Runtime.getRuntime().addShutdownHook(printingHook);

‘’’

这里演示在JVM关闭之前在控制台打印一些信息:

 
> System.exit(129); 
// In the middle of a shutdown 
 

我们看到钩子函数确实如期望在控制台打印了信息。

JVM负责启动钩子函数,如果钩子函数已经启动,JVM会抛出异常:

 
Thread longRunningHook = new Thread(() -> { 
    try { 
        Thread.sleep(300); 
    } catch (InterruptedException ignored) {} 
}); 
longRunningHook.start(); 
 
assertThatThrownBy(() -> Runtime.getRuntime().addShutdownHook(longRunningHook)) 
  .isInstanceOf(IllegalArgumentException.class) 
  .hasMessage("Hook already running"); 
 

显然,也不能多次注册一个钩子函数:

 
Thread unfortunateHook = new Thread(() -> {}); 
Runtime.getRuntime().addShutdownHook(unfortunateHook); 
 
assertThatThrownBy(() -> Runtime.getRuntime().addShutdownHook(unfortunateHook)) 
  .isInstanceOf(IllegalArgumentException.class) 
  .hasMessage("Hook previously registered"); 
 

删除钩子函数

Java 提供对应的删除函数用于删除已注册的关闭钩子函数:

 
Thread willNotRun = new Thread(() -> System.out.println("Won't run!")); 
Runtime.getRuntime().addShutdownHook(willNotRun); 
 
assertThat(Runtime.getRuntime().removeShutdownHook(willNotRun)).isTrue(); 
 

当成功删除钩子函数后,removeShutdownHook()函数返回true。

说明

JVM仅在正常关闭时才会运行钩子函数。因此当外部暴力kill jvm进程,并不会获得机会执行钩子函数。另外,从java代码暂停JVM也不会执行钩子函数:

 
Thread haltedHook = new Thread(() -> System.out.println("Halted abruptly")); 
Runtime.getRuntime().addShutdownHook(haltedHook); 
         
Runtime.getRuntime().halt(129); 

`halt()'方法强制终止当前运行的JVM,因此注册的钩子函数不会有机会执行。

总结

本文介绍关闭JVM的几种方式,然后使用运行时API注册或删除钩子函数。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/121596294
阅读延展