Spodziewałbym się, że JVM z wdziękiem przerywa ( thread.interrupt()
) wszystkie uruchomione wątki utworzone przez aplikację, przynajmniej dla sygnałów SIGINT (kill -2)
i SIGTERM (kill -15)
.
W ten sposób sygnał zostanie do nich przekazany, umożliwiając wdzięczne anulowanie wątku i finalizację zasobów w standardowy sposób .
Ale to nie jest tak (przynajmniej w moim implementacji 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)
.
Jak komentowali inni użytkownicy, użycie haków zamykających wydaje się obowiązkowe.
Jak więc sobie z tym poradzę?
Po pierwsze, nie przejmuję się tym we wszystkich programach, tylko w tych, w których chcę śledzić anulowania użytkowników i nieoczekiwane zakończenia. Na przykład wyobraź sobie, że twój program java jest procesem zarządzanym przez inne. Możesz chcieć rozróżnić, czy zostało zakończone z wdziękiem ( SIGTERM
z procesu menedżera), czy też nastąpiło zamknięcie (w celu automatycznego ponownego uruchomienia zadania podczas uruchamiania).
Jako podstawę, zawsze okresowo informuję moje długo działające wątki o stanie przerwania i wyrzucam, InterruptedException
jeśli zostały przerwane. Umożliwia to finalizację wykonania w sposób kontrolowany przez dewelopera (również z wynikiem identycznym jak standardowe operacje blokujące). Następnie na najwyższym poziomie stosu nici InterruptedException
jest przechwytywany i wykonywane jest odpowiednie czyszczenie. Wątki te są zakodowane, aby wiedzieć, jak odpowiedzieć na żądanie przerwania. Konstrukcja o wysokiej spójności .
Dlatego w takich przypadkach dodaję hak zamykający, który robi to, co myślę, że JVM powinna robić domyślnie: przerywa wszystkie wątki niebędące demonami utworzone przez moją aplikację, które nadal działają:
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");
}
});
Kompletna aplikacja testowa na github: https://github.com/idelvall/kill-test
kill -9
znaczy dla mnie: „Precz, plugawy proces, precz z tobą!”, po czym proces ten ustanie. Natychmiast.