1. 概要

通常、サービスを開始するのは簡単です。 ただし、場合によっては、1つを正常にシャットダウンするための計画を立てる必要があります。

このチュートリアルでは、JVMアプリケーションを終了するさまざまな方法を見ていきます。 次に、JavaAPIを使用してJVMシャットダウンフックを管理します。 JavaアプリケーションでJVMをシャットダウンする方法の詳細については、この記事を参照してください。

2. JVMシャットダウン

JVMは、次の2つの方法でシャットダウンできます。

  1. 制御されたプロセス
  2. 突然の方法

制御されたプロセスは、次のいずれかの場合にJVMをシャットダウンします。

  • 最後の非デーモンスレッドが終了します。 たとえば、メインスレッドが終了すると、JVMはシャットダウンプロセスを開始します
  • OSから割り込み信号を送信します。 たとえば、Ctrl + Cを押すか、OSからログオフします
  • JavaコードからSystem.exit()を呼び出す

私たち全員が適切なシャットダウンに努めていますが、JVMが突然予期しない方法でシャットダウンする場合があります。 次の場合にJVMが突然シャットダウンします

  • OSからキル信号を送信します。 たとえば、 -9を殺す
  • JavaコードからRuntime.getRuntime()。halt()を呼び出す
  • 停電やOSのパニックなどで、ホストOSが予期せず停止します。

3. シャットダウンフック

JVMを使用すると、シャットダウンが完了する前に登録機能を実行できます。 これらの機能は通常、リソースや他の同様のハウスキーピングタスクを解放するのに適した場所です。 JVMの用語では、これらの関数はシャットダウンフックと呼ばれます。

シャットダウンフックは基本的に初期化されていますが、スレッドは開始されていません。 JVMがシャットダウンプロセスを開始すると、登録されているすべてのフックが不特定の順序で開始されます。 すべてのフックを実行した後、JVMは停止します。

3.1. フックの追加

シャットダウンフックを追加するには、 Runtime.getRuntime()。addShutdownHook()メソッドを使用できます。

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

ここでは、JVMがシャットダウンする前に、標準出力に何かを出力するだけです。 次のようにJVMをシャットダウンした場合:

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

次に、フックが実際にメッセージを標準出力に出力することがわかります。

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

もちろん、フックを複数回登録することもできません。

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

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

3.2. フックの取り外し

Javaには、登録後に特定のシャットダウンフックを削除するためのツインremoveメソッドが用意されています。

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

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

removeShutdownHook()メソッドは、シャットダウンフックが正常に削除されると、trueを返します。

3.3. 警告

JVMは、通常の終了の場合にのみシャットダウンフックを実行します。 したがって、外力によってJVMプロセスが突然強制終了された場合、JVMはシャットダウンフックを実行する機会がありません。 さらに、JavaコードからJVMを停止しても、同じ効果があります。

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

hallt メソッドは、現在実行中のJVMを強制的に終了します。 したがって、登録されたシャットダウンフックは実行されません。

4. 結論

このチュートリアルでは、JVMアプリケーションが終了する可能性のあるさまざまな方法について説明しました。 次に、いくつかのランタイムAPIを使用して、シャットダウンフックを登録および登録解除しました。

いつものように、サンプルコードはGitHubから入手できます。