优雅地关闭线程池是一个涉及资源管理和代码健壮性的重要问题。在Java中,可以使用ExecutorService来创建和管理线程池,并使用其提供的方法来优雅地关闭线程池。以下是一些建议的步骤和注意事项:
1、调用shutdown()方法拒绝新提交的任务,已提交的任务不受影响。
调用shutdown()后,已提交的任务会继续执行,但线程池不再接收新任务。如果调用shutdown()时线程池已经关闭,那么这个方法不会有任何效果。
调用shutdown()后,不会阻塞等待已提交的任务执行完成,如果需要阻塞等待,需要调用awaitTermination()方法。
2、调用awaitTermination方法等待已提交任务完成。
在调用shutdown()之后,可以使用awaitTermination(long timeout, TimeUnit unit)方法来等待线程池中的所有任务都完成执行。
这个方法会阻塞当前线程,直到所有任务都完成执行、超时发生,或者当前线程被中断,以先发生者为准。
如果在超时时间内所有任务都完成了,那么这个方法会返回true;否则返回false。
3、处理未完成的任务,必要时调用shutdownNow()。
调用 awaitTermination(long timeout, TimeUnit unit)方法来等待线程池中的所有任务都完成执行,如果在超时时间内所有任务都完成了,那么这个方法会返回true;否则返回false,这时我们可以调用shutdownNow(),强制停止未执行完的任务。
调用shutdownNow()也不会阻塞等待强制停止未完成的任务,所以可以再次使用awaitTermination(long timeout, TimeUnit unit)方法等待。
4、异常处理
在关闭线程池的过程中,需要注意捕获和处理可能出现的异常,以确保程序的健壮性。
5、代码示例
dubbo3源码:
org.apache.dubbo.common.utils.ExecutorUtil#gracefulShutdown
public static void gracefulShutdown(Executor executor, int timeout) {
if (!(executor instanceof ExecutorService) || isTerminated(executor)) {
return;
}
final ExecutorService es = (ExecutorService) executor;
try {
// Disable new tasks from being submitted
es.shutdown();
} catch (SecurityException | NullPointerException ex2) {
return;
}
try {
// Wait a while for existing tasks to terminate
if (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
es.shutdownNow();
}
} catch (InterruptedException ex) {
es.shutdownNow();
Thread.currentThread().interrupt();
}
if (!isTerminated(es)) {
newThreadToCloseExecutor(es);
}
}
rocketmq5.0.0源码:
org.apache.rocketmq.common.utils.ThreadUtils#shutdownGracefully(java.util.concurrent.ExecutorService, long, java.util.concurrent.TimeUnit)
public static void shutdownGracefully(ExecutorService executor, long timeout, TimeUnit timeUnit) {
// Disable new tasks from being submitted.
executor.shutdown();
try {
// Wait a while for existing tasks to terminate.
if (!executor.awaitTermination(timeout, timeUnit)) {
executor.shutdownNow();
// Wait a while for tasks to respond to being cancelled.
if (!executor.awaitTermination(timeout, timeUnit)) {
LOGGER.warn(String.format("%s didn't terminate!", executor));
}
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted.
executor.shutdownNow();
// Preserve interrupt status.
Thread.currentThread().interrupt();
}
}