如何优雅关闭线程池

257 阅读2分钟

关闭线程池的几个方法

  • 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);
            }
        }
    }
}

image.png

image.png