JVM は、少なくともシグナルに対して、アプリケーションによって作成された実行中のすべてのスレッドを正常に中断する(thread.interrupt()
)と期待しますSIGINT (kill -2)
およびSIGTERM (kill -15)
。
このようにして、シグナルがそれらに転送され、標準的な方法でスレッドのキャンセルとリソースのファイナライズを適切に行うことができます。
しかし、これは当てはまりません(少なくとも私のJVM実装では:)Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
。
他のユーザーがコメントしたように、シャットダウンフックの使用は必須のようです。
それで、どうすればそれを処理できますか?
最初に、すべてのプログラムでそれを気にしません。ユーザーのキャンセルと予期しない終了を追跡したいプログラムでのみです。たとえば、Javaプログラムが他のプログラムによって管理されているプロセスだとします。正常に終了したか(SIGTERM
マネージャープロセスから)、シャットダウンが発生したか(起動時にジョブを自動的に再起動するため)を区別することができます。
基本として、私は常に、実行時間の長いスレッドに割り込みステータスを定期的に認識させ、割り込みが発生したInterruptedException
場合はをスローします。これにより、開発者が制御する方法で実行のファイナライズを行うことができます(これも標準のブロッキング操作と同じ結果になります)。次に、スレッドスタックの最上位でInterruptedException
キャプチャされ、適切なクリーンアップが実行されます。これらのスレッドは、割り込み要求に応答する方法がわかっているようにコード化されています。凝集度の高いデザイン。
したがって、これらのケースでは、シャットダウンフックを追加します。これにより、JVMがデフォルトで実行する必要があると思われる処理が実行されます。アプリケーションによって作成され、まだ実行中のすべての非デーモンスレッドに割り込みます。
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Interrupting threads");
Set<Thread> runningThreads = Thread.getAllStackTraces().keySet();
for (Thread th : runningThreads) {
if (th != Thread.currentThread()
&& !th.isDaemon()
&& th.getClass().getName().startsWith("org.brutusin")) {
System.out.println("Interrupting '" + th.getClass() + "' termination");
th.interrupt();
}
}
for (Thread th : runningThreads) {
try {
if (th != Thread.currentThread()
&& !th.isDaemon()
&& th.isInterrupted()) {
System.out.println("Waiting '" + th.getName() + "' termination");
th.join();
}
} catch (InterruptedException ex) {
System.out.println("Shutdown interrupted");
}
}
System.out.println("Shutdown finished");
}
});
githubで完全なテストアプリケーション:https : //github.com/idelvall/kill-test
kill -9
私にとっての意味:「立ち去った、汚いプロセス、あなたと離れて!」すぐに。