LoginSignup
35
35

More than 5 years have passed since last update.

ShutdownHookを使用する

Last updated at Posted at 2015-09-07

ShutdownHookとは

JVMが整然とシャットダウンされた場合には、JVMは登録されているシャットダウンフックを実行します。
整然としたシャットダウンとは以下のものを指します。

  • デーモンでないスレッドが全て終了した
  • System.exitが呼ばれた
  • ユーザ割り込みでの停止(Ctrl + C、SIGINT)

実装例

シャットダウンフックはRuntimeのaddShutdownHookメソッドで登録します。

ShutdownTest.java
package demo;

/**
 * Shutdownイベントのテストを行うクラスです。
 *
 */
public class ShutdownTest {

    public static void main(String[] args) {

        //シャットダウンフックを登録します。
        Runtime.getRuntime().addShutdownHook(new Thread(
                () -> System.out.println("shutdown..")
                ));

        System.out.println("processed");

    }
}

複数の処理を行うには

シャットダウンフックに登録した処理は並列で処理されます。

ShutdownTest2.java
package demo;

/**
 * Shutdownイベントのテストを行うクラスです。
 *
 */
public class ShutdownTest2 {

    public static void main(String[] args) {

        //シャットダウンフックを登録します。
        Runtime.getRuntime().addShutdownHook(new Thread(
                () -> {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("shutdown A..");
                }
                ));

        Runtime.getRuntime().addShutdownHook(new Thread(
                () -> System.out.println("shutdown B..")
                ));

        System.out.println("processed");

    }
}

実行すると、"shutdown B.."の方が先に出力されます。

以下のようにシャットダウンイベントを管理するクラスを実装することで、処理順を制御することができます。

ShutdownManager.java
package demo;

import java.util.ArrayList;
import java.util.List;

/**
 * シャットダウンイベントの管理を行うクラスです。
 * 
 */
public final class ShutdownManager implements Runnable {

    /** ShutdownManagerのSingletonインスタンス */
    private static final ShutdownManager instance = new ShutdownManager();
    private ShutdownManager() {}

    /** シャットダウンイベントのリスナー */
    private List<Runnable> listeners = new ArrayList<Runnable>();

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(instance));
    }

    /**
     * ShutdownManagerのインスタンスを返却します。
     * 
     * @return ShutdownManagerのインスタンス
     */
    public static ShutdownManager getInstance() {
        return instance;
    }

    /**
     * リスナーを追加します。
     * 
     * @param listener リスナー
     */
    public void addListeners(Runnable listener) {
        listeners.add(listener);
    }

    /**
     * シャットダウン処理を行います。
     */
    public void run() {
        System.out.println("shutdown start");
        listeners.forEach(Runnable::run);
        System.out.println("shutdown end");
    }

}
ShutdownTest3.java
package demo;

/**
 * Shutdownイベントのテストを行うクラスです。
 *
 */
public class ShutdownTest3 {

    public static void main(String[] args) {

        //シャットダウンフックを登録します。
        ShutdownManager.getInstance().addListeners(
                () -> {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("shutdown A..");
                }
                );

        ShutdownManager.getInstance().addListeners(
                () -> System.out.println("shutdown B..")
                );


        System.out.println("processed");

    }

}

注意

シャットダウンフックは開始されると他のスレッドと並行して動きます。
他のスレッドの終了を待ちません。

ShutdownTest4.java
package demo;

/**
 * Shutdownイベントのテストを行うクラスです。
 *
 */
public class ShutdownTest4 {

    public static void main(String[] args) {


        new Thread(
                () -> {
                    try {
                        System.out.println("start");
                        Thread.sleep(1000);
                        System.out.println("end");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                ).start();

        Runtime.getRuntime().addShutdownHook(new Thread(
                () -> {
                    try {
                        System.out.println("shutdown start");
                        Thread.sleep(1500);
                        System.out.println("shutdown end");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                ));

        System.exit(0);

    }
}

上の例ではスレッド動作中にSystem.exitしていますが、実行結果は次のようになります。

start
shutdown start
end
shutdown end

リソースの扱いなど、他のスレッドの状況に依存しないようにする必要があります。

ちなみに、System.exit(0)を削除すると以下のような結果になります。

start
end
shutdown start
shutdown end
35
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
35