優雅的關閉JAVA程序
背景
在線上Java程序中經常遇到進程程掛掉,一些狀態沒有正確的保存下來,這時候就需要在JVM關掉的時候執行一些清理現場的代碼。Java中得ShutdownHook提供了比較好的方案。
JDK在1.3之後提供了Java Runtime.addShutdownHook(Thread hook)方法,可以註冊一個JVM關閉的鉤子,這個鉤子可以在以下幾種場景被調用:
- 程序正常退出
- 使用System.exit()
- 終端使用Ctrl+C觸發的中斷
- 系統關閉
- 使用Kill pid命令幹掉進程
注:在使用kill -9 pid是不會JVM註冊的鉤子不會被調用。
什麼是Shutdown Hook
Shutdown hook是一個initialized but unstarted thread。當JVM開始執行shutdown sequence時,會併發運行所有registered Shutdown Hook。這時,在Shutdown Hook這個線程裏定義的操作便會開始執行。
需要注意的是,在Shutdown Hook裏執行的操作應當是不太耗時的。因爲在用戶註銷或者操作系統關機導致的JVM shutdown的例子中,系統只會預留有限的時間給未完成的工作,超時之後還是會強制關閉。
如何使用Shutdown Hook
調用java.lang.Runtime這個類的addShutdownHook(Thread hook)方法即可註冊一個Shutdown Hook,然後在Thread中定義需要在system exit時進行的操作。如下:
Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Do something in Shutdown Hook")));
測試代碼
public class ShutDownHook {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread()
{
@Override
public void run()
{
System.out.println("Interrupting threads");
Set<Thread> runningThreads = Thread.getAllStackTraces().keySet();
for (Thread runningThread : runningThreads) {
// 判斷線程是否是我們要測試的線程
if (runningThread.getName().equals("InteruptThread")) {
// 如果線程沒有被中斷,我們進行中斷他
if (!runningThread.isInterrupted()) {
System.out.println("InteruptThread is not interrupted, we are going to interupt it");
runningThread.interrupt();
}
}
}
System.out.println("Shutdown hook ran!");
}
});
// 創建並啓動子線程類
InteruptThread thread = new InteruptThread();
thread.start();
}
/**
* 用來測試要中斷的線程類
*/
static class InteruptThread extends Thread {
public InteruptThread() {
this.setName("InteruptThread");
}
@Override
public void run() {
while (true) {
try {
while (true) {
// 打印個時間
System.out.println(System.currentTimeMillis());
// 等待10秒
TimeUnit.SECONDS.sleep(10);
}
} catch (InterruptedException e) {
System.out.println("oh yeah, InteruptThread is interrupted");
break;
} catch (Exception e) {
System.out.println("unknown exception ocurred");
break;
}
}
}
}
}