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