Eu esperaria que a JVM interrompa ( thread.interrupt()
) normalmente todos os threads em execução criados pelo aplicativo, pelo menos para sinais SIGINT (kill -2)
e SIGTERM (kill -15)
.
Dessa forma, o sinal será encaminhado a eles, permitindo um cancelamento de thread e finalização de recursos nas formas padrão .
Mas este não é o caso (pelo menos na minha implementação 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)
.
Como outros usuários comentaram, o uso de ganchos de desligamento parece obrigatório.
Então, como eu lidaria com isso?
Bem, em primeiro lugar, eu não me importo com isso em todos os programas, apenas naqueles em que desejo manter o controle de cancelamentos de usuários e finais inesperados. Por exemplo, imagine que seu programa java é um processo gerenciado por outro. Você pode querer diferenciar se ele foi encerrado normalmente (a SIGTERM
partir do processo do gerenciador) ou se ocorreu um encerramento (para reiniciar automaticamente o trabalho na inicialização).
Como base, eu sempre deixo meus threads de longa duração periodicamente cientes do status de interrupção e lanço um InterruptedException
se eles foram interrompidos. Isso permite a finalização da execução de forma controlada pelo desenvolvedor (também produzindo o mesmo resultado que as operações de bloqueio padrão). Então, no nível superior da pilha de encadeamentos, InterruptedException
é capturado e a limpeza apropriada é realizada. Esses threads são codificados para saber como responder a uma solicitação de interrupção. Design de alta coesão .
Portanto, nesses casos, adiciono um gancho de desligamento, que faz o que acho que a JVM deve fazer por padrão: interromper todos os encadeamentos não daemon criados por meu aplicativo que ainda estão em execução:
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");
}
});
Aplicação de teste completa no github: https://github.com/idelvall/kill-test
kill -9
significa para mim: "Vá embora, processo sujo, vá embora!", quando o processo cessará. Imediatamente.