Я ожидал, что 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
означает для меня: «Уходи, гнусный процесс, прочь с собой!», после чего процесс прекратит свое существование. Немедленно.