これまでちゃんと使ったことがなかったRuntime#addShudownHookと、個人的にメモとして書き残しておきたいsun.misc.SignalHandlerについて、ちょっと書いておきます。
それぞれどんなものなのかですが、Runtime#addShutdownHookはJavaアプリケーションの終了時に実行させるThreadを登録することができるものです。これを使うことで、Javaアプリケーションの終了時に何らかの処理を実行することができます。まあ、いついかなる時も実行されるというわけではありませんが。
sun.misc.SignaHandlerは、sum.miscという推奨されないパッケージですが、Javaでシグナルを扱うことができる(シグナルハンドラを登録することができる)ようになります。
以下に、順に使っていってみましょう。
推奨は、Runtime#addShutdownHookかと思いますので、こちらから。
なお、実行環境はUbuntu Linux 14.04 LTSで、kill -lが以下のように表示されます。
$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
Runtime#addShutdownHook
Runtimeのインスタンスの、addShutdownHookにThreadを登録することで、Javaアプリケーションの終了時に登録したスレッドを実行させることができます。
今回は、シグナルを送って反応するかどうかを確認してみました。
Integrating Signal and Exception Handling
http://www.oracle.com/technetwork/java/javase/signals-139944.html
このドキュメントによると、SIGTERM、SIGINT、SIGHUPに対して有効と書かれていますが、実際その通りでした。
サンプルとして作成したコード。
ShutdownHookTest.java
import java.io.Console; public class ShutdownHookTest { public static void main(String... args) { Runtime .getRuntime() .addShutdownHook(new Thread(() -> System.out.println("Run Shutdown Hook."))); Console console = System.console(); console.readLine("Enter stop this program.\n"); } }
起動すると、このようにEnterを入力するまで終了を待機します。
$ java ShutdownHookTest
Enter stop this program.
このプロセスに対して、killコマンドでシグナルを送ってみます。
$ kill -HUP [PID]
すると、addShutdownHookで登録した処理が実行されて終了しました。
$ java ShutdownHookTest
Enter stop this program.
Run Shutdown Hook.
機能していますね。
繰り返しますが、反応したシグナルはSIGTERM、SIGINT、SIGHUPとなります。
sun.misc.SignalHandler
sun.miscという非推奨パッケージに属するクラスですが、このSignalHandlerおよびSignalを使うことで、シグナルをハンドリングすることができます。
使い方は、こんな感じです。
Signal.handle(new Signal("INT"), new SignalHandler() { public void handle(Signal signal) { // シグナルを受け取って何か処理 } }
Signalに対して、SignalHandlerインターフェースを実装したクラスを登録します。
で、うちの環境で「kill -l」で表示されたシグナルに対して、できる限りSignalHandlerを登録してみました。
できあがったソースコードは、こちら。
SignalHandlerTest.java
import java.io.Console; import java.util.Arrays; import sun.misc.Signal; import sun.misc.SignalHandler; public class SignalHandlerTest { public static void main(String... args) { Signal[] signals = { new Signal("HUP"), new Signal("INT"), // new Signal("QUIT"), // java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGQUIT // new Signal("ILL"), // java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGILL new Signal("TRAP"), new Signal("ABRT"), new Signal("BUS"), // new Signal("FPE"), // java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGFPE // new Signal("KILL"), // java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGKILL // new Signal("USR1"), // java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGUSR1 // new Signal("SEGV"), // java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGSEGV new Signal("USR2"), new Signal("PIPE"), new Signal("ALRM"), new Signal("TERM"), new Signal("STKFLT"), new Signal("CHLD"), new Signal("CONT"), // new Signal("STOP"), // java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGSTOP new Signal("TSTP"), new Signal("TTIN"), new Signal("TTOU"), new Signal("URG"), new Signal("XCPU"), new Signal("XFSZ"), new Signal("VTALRM"), new Signal("PROF"), new Signal("WINCH"), new Signal("IO"), new Signal("PWR"), new Signal("SYS") // ここから下は、Unknown扱い // Unknown signal: RTMAX // new Signal("RTMIN"), // new Signal("RTMIN+1"), // new Signal("RTMIN+2"), // new Signal("RTMIN+3"), // new Signal("RTMIN+4"), // new Signal("RTMIN+5"), // new Signal("RTMIN+6"), // new Signal("RTMIN+7"), // new Signal("RTMIN+8"), // new Signal("RTMIN+9"), // new Signal("RTMIN+10"), // new Signal("RTMIN+11"), // new Signal("RTMIN+12"), // new Signal("RTMIN+13"), // new Signal("RTMIN+14"), // new Signal("RTMIN+15"), // new Signal("RTMAX-14"), // new Signal("RTMAX-13"), // new Signal("RTMAX-12"), // new Signal("RTMAX-11"), // new Signal("RTMAX-10"), // new Signal("RTMAX-9"), // new Signal("RTMAX-8"), // new Signal("RTMAX-7"), // new Signal("RTMAX-6"), // new Signal("RTMAX-5"), // new Signal("RTMAX-4"), // new Signal("RTMAX-3"), // new Signal("RTMAX-2"), // new Signal("RTMAX-1"), // new Signal("RTMAX"), }; SignalHandler handler = new MyHandler(); Arrays .stream(signals) .forEach(s -> Signal.handle(s, handler)); Console console = System.console(); console.readLine("Enter stop this program.\n"); } private static class MyHandler implements SignalHandler { @Override public void handle(Signal signal) { System.out.printf("Trap Signal name = [%s], number = [%d]%n", signal.getName(), signal.getNumber()); } } }
SignalHandlerを実装したクラスには、単にシグナルの名前と値を出力させるようにしました。
private static class MyHandler implements SignalHandler { @Override public void handle(Signal signal) { System.out.printf("Trap Signal name = [%s], number = [%d]%n", signal.getName(), signal.getNumber()); } }
コメントアウトしているのは、Java VMやOSによって予約されており、登録できなかったシグナルです。
つまり、sun.misc.SignalHandlerを使ってハンドリングできるのは、以下だということです。
シグナル | 値 |
---|---|
SIGHUP | 1 |
SIGINT | 2 |
SIGTRAP | 5 |
SIGABRT | 6 |
SIGBUS | 7 |
SIGUSR2 | 12 |
SIGPIPE | 13 |
SIGALRM | 14 |
SIGTERM | 15 |
SIGSTKFLT | 16 |
SIGCHLD | 17 |
SIGCONT | 18 |
SIGTSTP | 20 |
SIGTTIN | 21 |
SIGTTOU | 22 |
SIGURG | 23 |
SIGXCPU | 24 |
SIGXFSZ | 25 |
SIGVTALRM | 26 |
SIGPROF | 27 |
SIGWINCH | 28 |
SIGIO | 29 |
SIGPWR | 30 |
SIGSYS | 31 |
さすがに、SIGKILLなどはムリだということですね。
では、作成したプログラムを起動してみます。
$ java SignalHandlerTest
Enter stop this program.
このプログラムに対して、登録したシグナルを送ってみます。
$ kill -HUP [PID] $ kill -INT [PID] $ kill -TRAP [PID] $ kill -ABRT [PID] $ kill -BUS [PID] $ kill -USR2 [PID] $ kill -PIPE [PID] $ kill -ALRM [PID] $ kill -TERM [PID] $ kill -STKFLT [PID] $ kill -CHLD [PID] $ kill -CONT [PID] $ kill -TSTP [PID] $ kill -TTIN [PID] $ kill -TTOU [PID] $ kill -URG [PID] $ kill -XCPU [PID] $ kill -XFSZ [PID] $ kill -VTALRM [PID] $ kill -PROF [PID] $ kill -WINCH [PID] $ kill -IO [PID] $ kill -PWR [PID] $ kill -SYS [PID]
結果は、このような感じに。
$ java MySignalHandler Enter stop this program. Trap Signal name = [HUP], number = [1] Trap Signal name = [INT], number = [2] Trap Signal name = [TRAP], number = [5] Trap Signal name = [ABRT], number = [6] Trap Signal name = [BUS], number = [7] Trap Signal name = [USR2], number = [12] Trap Signal name = [PIPE], number = [13] Trap Signal name = [ALRM], number = [14] Trap Signal name = [TERM], number = [15] Trap Signal name = [STKFLT], number = [16] Trap Signal name = [CHLD], number = [17] Trap Signal name = [CONT], number = [18] Trap Signal name = [TSTP], number = [20] Trap Signal name = [TTIN], number = [21] Trap Signal name = [TTOU], number = [22] Trap Signal name = [URG], number = [23] Trap Signal name = [XCPU], number = [24] Trap Signal name = [XFSZ], number = [25] Trap Signal name = [VTALRM], number = [26] Trap Signal name = [PROF], number = [27] Trap Signal name = [WINCH], number = [28] Trap Signal name = [IO], number = [29] Trap Signal name = [PWR], number = [30] Trap Signal name = [SYS], number = [31]
ちゃんと、シグナルがハンドリングできていますね。
なお、このプログラムはCtr-cで終了しなくなりますので、ご注意を(笑)。
^CTrap Signal name = [INT], number = [2]
というわけで、だいたいの使い方はわかりました。
基本的にはRuntime#addShutdownHookを使うと思いますが、備忘録的に。
合わせて覚えたい
「-Xrs」オプションを付与してJavaアプリケーションを起動すると、シグナルのトラップの仕方がまた変わるようです。
詳しくは、このあたりを参考に。
Java SEツール・リファレンス(UNIX) - 5 アプリケーションの作成と構築 java
http://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/java.html#BGBCIEFC
Java SEツール・リファレンス(Windows) - 5 アプリケーションの作成と構築 java
http://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/java.html#BGBCIEFC
Javaアプリケーションをログオフ中も実行させ続けるには
http://www.atmarkit.co.jp/fwin2k/win2ktips/654javaxrs/javaxrs.html