Shutdown Hook(關閉鉤子)是已初始化但尚未啟動的執行緒(Thread),向 Runtime 註冊後,當 JVM 即將正常關閉時,會並發啟動所有掛鉤執行緒執行清理工作。適用於:釋放資源、持久化狀態、關閉連線池、記錄審計日誌等「短暫、必要」的收尾任務。
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// 清理邏輯
logger.info("Shutdown hook executing...");
cleanupResources();
}));
JVM 正常關閉觸發條件(任一即可):
System.exit(int)public class ShutdownHookDemo implements Runnable {
public ShutdownHookDemo() {
Runtime.getRuntime().addShutdownHook(new Thread(this));
System.out.println(">>> Shutdown hook registered");
}
@Override
public void run() {
System.out.println("\n>>> Executing shutdown cleanup...");
cleanUp();
System.out.println(">>> Cleanup finished.");
}
private void cleanUp() {
for (int i = 0; i < 7; i++) {
System.out.println("Cleanup step " + i);
try { Thread.sleep(200); } catch (InterruptedException ignored) {}
}
}
public static void main(String[] args) throws InterruptedException {
new ShutdownHookDemo();
System.out.println(">>> Sleeping 5 sec, try Ctrl+C now...");
Thread.sleep(5000);
System.out.println(">>> Main thread exiting normally.");
}
}
多個掛鉤 並發啟動,無順序保證。若有依賴關係,需自行協調(如單一掛鉤內按序呼叫、使用 CountDownLatch 等)。
JVM 關閉過程中,作業系統可能強制終止進程(如 Windows 服務關閉超時、Kubernetes terminationGracePeriodSeconds)。長時間任務風險極高,建議 < 5 秒 內完成。
Runtime.halt() 強制終止(不運行關閉鉤、不執行 finally、不執行 finalizer)kill -9)掛鉤執行緒在 start() 前不佔用系統執行緒資源(大多數 JVM 實作)。註冊成百上千個掛鉤僅佔用少量堆記憶體儲存 Thread 物件,無效能顧慮。
掛鉤執行緒拋出未捕獲異常會印堆疊追蹤但 不會阻止其他掛鉤執行。建議在 run() 內部 try-catch 吞噬異常,避免汙染日誌。
close() 多次安全)。ExecutorService + Future.get(timeout) 包裝清理任務,防止卡死。@Component
public class GracefulShutdown implements DisposableBean, ApplicationListener {
private final ThreadPoolTaskExecutor taskExecutor;
public GracefulShutdown(ThreadPoolTaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
// 註冊 JVM 掛鉤作為雙重保險
Runtime.getRuntime().addShutdownHook(new Thread(this::doShutdown));
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
doShutdown(); // Spring 關閉時觸發
}
@Override
public void destroy() throws Exception {
doShutdown(); // Bean 銷毀時觸發
}
private void doShutdown() {
System.out.println(">>> Graceful shutdown initiated...");
taskExecutor.shutdown();
try {
if (!taskExecutor.getThreadPoolExecutor().awaitTermination(10, TimeUnit.SECONDS)) {
taskExecutor.getThreadPoolExecutor().shutdownNow();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
taskExecutor.getThreadPoolExecutor().shutdownNow();
}
System.out.println(">>> Thread pool terminated.");
}
}
Shutdown Hook 是 JVM 提供的「最後防線」,讓應用在正常關閉時有機會體面收尾。但它不保證一定執行,核心資料持久化、分散式協調等關鍵邏輯,必須在業務層面設計冪等、可重試、可補償機制,不能寄望於關閉鉤。正確心態:有則優雅,無則不影響正確性。