利用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的钩子函数实践调用

原文地址:https://www.cnblogs.com/deepSleeping/p/14976815.html