多執行緒應用中,主執行緒結束或 System.exit() 被呼叫時,JVM 會啟動關機序列。若無掛鉤,資料庫連線池、執行緒池、暫存檔、記憶體快取等資源可能來不及釋放,導致資料不一致或資源洩漏。
Runtime.getRuntime().addShutdownHook(Thread hook)
removeShutdownHook 可移除已註冊 Hook。Runtime.halt() 會 不執行 Hook 直接終止。public class GracefulShutdown {
private static final Logger LOG = LoggerFactory.getLogger(GracefulShutdown.class);
public static void main(String[] args) {
// 1. 註冊關機鉤子(盡早註冊)
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOG.info("=== 應用程式關機序列啟動 ===");
try {
// 2. 關閉資源:資料庫連線池、HTTP Client、執行緒池、MQ 連線...
DataSourcePool.shutdown(); // 例:HikariCP.shutdown()
ThreadPoolManager.shutdown(); // 例:executor.shutdown() + awaitTermination
CacheManager.persistToDisk(); // 熱資料落盤
TempFileCleaner.cleanup(); // 刪除暫存檔
LOG.info("所有資源已釋放");
} catch (Exception e) {
LOG.error("關機過程發生錯誤", e);
}
}, "Shutdown-Hook"));
// 3. 主程式邏輯
Application app = new Application();
app.start(); // 內部可能啟動 Web Server、排程器等
}
}
Spring 容器關機時自動呼叫 DisposableBean.destroy()、@PreDestroy、SmartLifecycle.stop(),無需手寫 Hook。若需補充:
@Component
public class CustomShutdownHook {
@Autowired DataSource dataSource;
@Autowired ThreadPoolTaskExecutor taskExecutor;
@PreDestroy
public void onShutdown() {
if (dataSource instanceof HikariDataSource) ((HikariDataSource)dataSource).close();
taskExecutor.shutdown();
}
}
CountDownLatch 或 CompletableFuture 等待關鍵步驟,設定最大等待時間。System.exit(0) 驗證 Hook 邏輯;整合測試用 kill -TERM 驗證容器層面關機。finalize() 釋放資源 → 不可靠、已淘汰。