在關閉java程序前完成某些操作(釋放資源,處理內存中的數據等)

優雅的關閉JAVA程序

背景

在線上Java程序中經常遇到進程程掛掉,一些狀態沒有正確的保存下來,這時候就需要在JVM關掉的時候執行一些清理現場的代碼。Java中得ShutdownHook提供了比較好的方案。
  JDK在1.3之後提供了Java Runtime.addShutdownHook(Thread hook)方法,可以註冊一個JVM關閉的鉤子,這個鉤子可以在以下幾種場景被調用:

  1. 程序正常退出
  2. 使用System.exit()
  3. 終端使用Ctrl+C觸發的中斷
  4. 系統關閉
  5. 使用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;
                    }

                }

            }
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章