利用JVM鉤子函數優雅關閉線程池

一、如何優雅關閉線程池

核心API:

  • shutDown
  • shutDownNow
  • awaitTermination

利用JVM鉤子函數,在虛擬機關閉時調用相關方法即”優雅關閉線程池”。

先通過shutdown等待線程池自身結束,然後等待一段時間,如果沒有成功,再調用shutdownNow將等待I/O的任務中斷並退出。


    /**
     * 添加鉤子函數
     */
    private void initGracefullyShutDown() {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> shutDownThreadPool(asyncExecutor, "BASE")));
        aliasExecutors.forEach((alias, threadPoolExecutor) ->
                Runtime.getRuntime().addShutdownHook(new Thread(() -> shutDownThreadPool(threadPoolExecutor, alias)))
        );
    }

    /**
    * 優雅關閉線程池。
    *  自身關閉,await 60s,強制關閉。
    */
    private void shutDownThreadPool(ExecutorService threadPool, String alias) {
        log.info("Start to shutdown the thead pool : {}", alias);

        threadPool.shutdown();
        try {
            if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                threadPool.shutdownNow();
                log.warn("Interrupt the worker, which may cause some task inconsistent");

                if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                    log.warn("Thread pool can't be shutdown even with interrupting worker threads, which may cause some task inconsistent.");
                }
            }
        } catch (InterruptedException ie) {
            threadPool.shutdownNow();
            log.warn("The current server thread is interrupted when it is trying to stop the worker threads. This may leave an inconsistent state.");

            Thread.currentThread().interrupt();
        }
    }

備註:本來是循環調用shutDownThreadPool()方法, 後來發現阻塞嚴重,追了下源碼修改成了循環添加鉤子了,具體看如下。

二、其他

JVM鉤子函數

執行時機

自身沒有細追源碼,簡單看了幾篇其他夥伴記錄的博客。

  • 虛擬機退出 :JVM會在所有非守護(後臺)線程關閉後纔會退出 (關於守護線程,隨便看了幾篇博客回憶下,這篇還不錯。《大白話講解守護線程》)
  • 系統調用System.exit(0)
  • JVM正常退出(Linux上kill命令也會調用鉤子函數)

追了一下源碼,看了下,發現是一個集合維護Threads。(即咱們調用API,add進去的)

ApplicationShutdownHooks.java

    /* Iterates over all application hooks creating a new thread for each
     * to run in. Hooks are run concurrently and this method waits for
     * them to finish.
     */
    static void runHooks() {
        Collection<Thread> threads;
        synchronized(ApplicationShutdownHooks.class) {
            threads = hooks.keySet();
            hooks = null;
        }

        for (Thread hook : threads) {
            hook.start();
        }
        for (Thread hook : threads) {
            while (true) {
                try {
                    hook.join();
                    break;
                } catch (InterruptedException ignored) {
                }
            }
        }
    }
}

參考 《可伸縮服務架構 框架與中間件》
參考 JVM的鉤子函數實踐調用

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