Я думаю, что здесь происходит то, что семантика ловушки завершения работы не очень хорошо документирована (не удалось найти это нигде в официальных документах или в постах блога).
Я думаю, что это происходит из-за того, что JVM завершает работу сразу после того, как все потоки перехватчиков завершения вернулись (в случае получения сигнала) , не дожидаясь завершения основного потока .
Вы можете смоделировать это с помощью следующей небольшой программы:
package cosenmarco;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
private static volatile CompletableFuture<Void> shutdownFuture = new CompletableFuture<>();
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
shutdownFuture.complete(null);
System.err.println("Shutdown signal");
}));
try {
shutdownFuture.get();
Thread.sleep(1000); // Comment this to have "Finished" correctly printed
System.err.println("Finished");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Также никакого прерывания потока не происходит: я думаю, что поток на самом деле просто завершен.
Что вы, возможно, захотите сделать (я где-то видел подобный код, но не могу вспомнить его прямо сейчас), это получить ссылку на основной поток и присоединиться к нему в хуке shutdown. Как-то так у меня работает:
package cosenmarco;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
private static volatile CompletableFuture<Void> shutdownFuture = new CompletableFuture<>();
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
shutdownFuture.complete(null);
System.err.println("Shutdown signal");
try {
mainThread.join();
System.err.println("Joined on main thread.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
try {
shutdownFuture.get();
Thread.sleep(1000);
System.err.println("Finished");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Это печатает (после нажатия Ctrl + C) следующее:
^CShutdown signal
Finished
Joined on main thread.