Adding Shutdown Hooks for JVM Applications – 为JVM应用程序添加关机钩子

最后修改: 2019年 12月 9日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

It’s typically easy to start-up a service. However, sometimes we need to have a plan for gracefully shutting one down.

通常,启动一项服务是很容易的。然而,有时我们需要有一个计划来优雅地关闭一个服务。

In this tutorial, we’re going to take a look at different ways a JVM application can terminate. Then, we’ll use Java APIs to manage JVM shutdown hooks. Please refer to this article to learn more about shutting down the JVM in Java applications.

在本教程中,我们将看一下JVM应用程序终止的不同方式。然后,我们将使用Java APIs来管理JVM关机钩子。请参考这篇文章以了解更多关于在Java应用程序中关闭JVM的信息。

2. JVM Shutdown

2.JVM关闭

The JVM can be shut down in two different ways:

JVM可以通过两种不同的方式被关闭。

  1. A controlled process
  2. An abrupt manner

A controlled process shuts down the JVM when either:

当以下两种情况之一发生时,受控进程将关闭JVM:

  • The last non-daemon thread terminates. For example, when the main thread exits, the JVM starts its shutdown process
  • Sending an interrupt signal from the OS. For instance, by pressing Ctrl + C or logging off the OS
  • Calling System.exit() from Java code

While we all strive for graceful shutdowns, sometimes the JVM may shut down in an abrupt and unexpected manner. The JVM shuts down abruptly when:

虽然我们都在努力追求优雅的关闭,但有时JVM可能会以一种突然的、意想不到的方式关闭。时,JVM会突然关闭。

  • Sending a kill signal from the OS. For example, by issuing a kill -9 <jvm_pid>
  • Calling Runtime.getRuntime().halt() from Java code
  • The host OS dies unexpectedly, for example, in a power failure or OS panic

3. Shutdown Hooks

3.关机钩

The JVM allows registering functions to run before it completes its shutdown. These functions are usually a good place for releasing resources or other similar house-keeping tasks. In JVM terminology, these functions are called shutdown hooks.

JVM允许在完成关机之前运行注册函数。这些函数通常是释放资源或其他类似的内部管理任务的好地方。在JVM的术语中,这些函数被称为shutdown hooks

Shutdown hooks are basically initialized but unstarted threads. When the JVM begins its shutdown process, it will start all registered hooks in an unspecified order. After running all hooks, the JVM will halt.

关机钩子基本上是初始化但未启动的线程。当JVM开始其关闭过程时,它将以一个未指定的顺序启动所有注册的钩子。在运行所有钩子之后,JVM将停止运行。

3.1. Adding Hooks

3.1.添加钩子

In order to add a shutdown hook, we can use the Runtime.getRuntime().addShutdownHook() method:

为了添加一个关闭钩子,我们可以使用Runtime.getRuntime().addShutdownHook()方法。

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

Here we simply print something to the standard output before JVM shuts down itself. If we shut down the JVM like following:

在这里,我们只是在JVM自行关闭之前向标准输出端打印一些东西。如果我们像下面这样关闭JVM。

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

Then we’ll see that the hook actually prints the message to standard output.

然后我们将看到,钩子实际上是将信息打印到标准输出。

The JVM is responsible for starting hook threads. Therefore, if the given hook has been already started, Java will throw an exception:

JVM负责启动钩子线程。因此,如果给定的钩子已经被启动了,Java将抛出一个异常。

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");

Obviously, we also can’t register a hook multiple times:

很明显,我们也不能多次注册一个钩子。

Thread unfortunateHook = new Thread(() -> {});
Runtime.getRuntime().addShutdownHook(unfortunateHook);

assertThatThrownBy(() -> Runtime.getRuntime().addShutdownHook(unfortunateHook))
  .isInstanceOf(IllegalArgumentException.class)
  .hasMessage("Hook previously registered");

3.2. Removing Hooks

3.2.移除钩子

Java provides a twin remove method to remove a particular shutdown hook after registering it:

Java提供了一个孪生的remove方法,以便在注册一个特定的关机钩子后将其删除。

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

assertThat(Runtime.getRuntime().removeShutdownHook(willNotRun)).isTrue();

The removeShutdownHook() method returns true when the shutdown hook is successfully removed.

removeShutdownHook()方法在成功删除关机钩时返回true

3.3. Caveats

3.3.注意事项

The JVM runs shutdown hooks only in case of normal terminations. So, when an external force kills the JVM process abruptly, the JVM won’t get a chance to execute shutdown hooks. Additionally, halting the JVM from Java code will also have the same effect:

JVM只在正常终止的情况下运行关闭钩子。因此,当外部力量突然杀死JVM进程时,JVM将没有机会执行关闭钩子。此外,从Java代码中停止JVM也会产生同样的效果。

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

The halt method forcibly terminates the currently running JVM. Therefore, registered shutdown hooks won’t get a chance to execute.

halt方法会强行终止当前运行的JVM。因此,注册的关机钩子不会有机会执行。

4. Conclusion

4.总结

In this tutorial, we looked at different ways a JVM application can possibly terminate. Then, we used a few runtime APIs to register and de-register shutdown hooks.

在本教程中,我们研究了JVM应用程序可能终止的不同方式。然后,我们使用一些运行时API来注册和取消注册关机钩子。

As usual, the sample code is available over on GitHub.

像往常一样,样本代码可在GitHub上获得。