关闭线程池的几个方法:
-
shutdown:线程池状态变为 SHUTDOWN - 不会接收新任务 - 但已提交和等待的任务会执行完 - 此方法不会阻塞调用线程的执行。
-
shutdownNow:线程池状态变为 STOP - 不会接收新任务 - - 并用 interrupt 的方式中断正在执行的任务。
-
boolean awaitTermination(long timeout, TimeUnit unit):调用 shutdown 后,由于调用线程并不会等待所有任务运行结束(只令已提交的任务执行完),因此可调用该方法等待指定时间,若线程执行完毕返回true。
说明: 由于线程池中关闭具有shutDown()的优雅关闭和shutDownNow()的强制关闭,我们如果想要优雅的关闭线程池,这两个方法都需要用上,当线程池接收到shutDown()命令后,会继续执行正在运行的任务和队列中等待的任务,但是我们不应该允许永远执行下去,此时就需要超过一定时间直接使用shutDownNow()进行强制关闭!
@Slf4j
public class ThreadPoolUtils {
private ThreadPoolUtils() {}
public static void shutDownPool(ExecutorService pool, int shutDownTimeOut,
int shutDownNowTimeOut, TimeUnit timeUnit) {
pool.shutdown(); //先调用一次shutdown
try {
if(!pool.awaitTermination(shutDownTimeOut, timeUnit)) { //判断指定时间后的线程池是否关闭
pool.shutdownNow(); //如果超时还未关闭,则暴力关闭
if(!pool.awaitTermination(shutDownNowTimeOut, timeUnit)) { //暴力关闭后,等待指定时间后再检验线程池是否关闭
log.error("ThreadPoolUtils.shutDownPool.erroe"); //暴力还未关闭,则证明线程池存在问题
}
}
} catch (InterruptedException e) {
// 外部线程被打断了,就会抛出该打断异常
log.error("ThreadPoolUtils.shutDownPool.interrupted.error:{}", e.getMessage(), e);
// 直接把线程池关闭
pool.shutdownNow();
// 直接将当前关闭线程给中断
Thread.currentThread().interrupt();
}
}
}
测试:
注意: 可以看到,执行shutdown()命令进行线程池关闭,只会执行正在执行的任务和等待队列中的任务;后续提交的任务都会被打断,直到在1000ms之后,会执行shutdownNow()命令强制关闭数据库,强制打断正在执行的任务并抛出sleep interrupted异常;;如果线程池关闭成功,后续任务再提交便会直接报错!
@Test
public void ttest() {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(new TaskShutDownPool());
}
ThreadPoolUtils.shutDownPool(executorService, 1000, 500, TimeUnit.MILLISECONDS);
executorService.execute(new TaskShutDownPool());
}
class TaskShutDownPool implements Runnable {
@Override
public void run() {
try {
Thread.sleep(500);
log.info(Thread.currentThread().getName());
} catch (InterruptedException e) {
log.info("TaskShutDownPool.interrupted:{}", e.getMessage(), e);
}
}
}
}