ShutdownHook

プロセスを外部から終了させたい場合,Unix系OSならkillコマンドや[ctrl+c]キーを使用する.このような時,プロセスにはシグナルという一種のイベントが通知される.そのシグナルを受け取ったときに行なう処理を予め実装しておけば,終了処理をちゃんと行なってからプロセスを終了できるようになる.データベース等の外部のプログラムを利用しているプログラムや,プログラムの状態を自分自身で管理しているようなプログラムの場合,こういった仕組みを使って終了処理を行なえないと,後で困ったことになる.

この話はJBossについても同じことが言える.JBossJVMというプロセスの内部で仮想的に実行されるプログラムであるため,このシグナルの利用というのは,JVMを経由しての利用ということになる.裏を返すと,JVMが提供してくれているもののみ,こういった仕組みを利用できる.

JVMが提供しているものの一つに,シャットダウンフックという仕組みがある.これはkillコマンドや[ctrl+c]キーの入力が行なわれて,そのシグナルをJVMプロセスが受け取った時,任意のJavaプログラムを起動させる仕組みである.

java.lang.Runtimeクラスに,addShutdownHook(Thread t)というメソッドがある.引数はjava.lang.Thread型のインスタンスである.以下のように定義しておくと,killコマンドや[ctrl+c]キーの入力の時に,run()メソッドを呼び出してくれる.

public class Foo {
    public static void main(String[] args) throws Exception {
        Foo foo = new Foo();
        foo.method();
    }

    public void method() throws Exception {
        Bar bar = new Bar();
        Runtime.getRuntime().addShutdownHook(bar);
        // 100秒寝る
        Thread.sleep(100*1000);
    }

    private class Bar extends Thread {
        public void run() {
            System.out.println("Hello! FooBar");
        }
    }
}

上記の実行イメージは,Fooクラスをmainメソッドから起動させて,100秒経って終わるまでの間に[ctrl+c]キーを押すと,シャットダウンフックにより"Hello! FooBar"と表示されるような感じ.

で,ここで疑問が一つ.JBossのコード(org.jboss.system.server.ServerImple)によると,barインスタンスをsetDaemon(true)してからaddShutdownHook(bar)するやり方をしているのだけど,これは何故?

setDaemon(true)もsetDaemon(false)(デフォルト)も試して見たけど,動きとしては違いが良く分からなかった.ユーザスレッドの場合はこれが生きている間はJVMは終了シーケンスを開始しないはず,ということから,MBeanServerからすべてのMBeanを削除といった時間のかかる終了処理をJBossが行なうにはsetDaemon(false)のほうが合っている気が素人目には見えるのだけど.

謎.