概念

ExecutorService关于关闭主要有如下几个方法

  • shutdown:在线程池队列中的提交的任务会执行,无法提交新的任务,注意调用这个方法,线程池不会等待(wait)在执行的任务执行完成,可以使用awaitTermination实现这个目的。这里需要注意的是:在执行的任务因为是异步线程执行的,任务还是会继续执行,只是说线程池不会阻塞等待任务执行完成
  • List<Runnable> shutdownNow():试图关闭正在执行的任务,不会执行已经提交到队列但是还没有执行的任务,返回等待执行的任务列表,同时此方法不会等待那些正在执行的任务执行完,等待执行的任务会从线程池队列移除。
  • isShutdown:线程池是否关闭
  • isTerminated :判断线程池关闭后所有的任务是否都执行完了,注意这个方法只有在 shutdownshutdownNow方法调用后才有效
  • awaitTermination:阻塞,直到一下任务情况出现:
  • shutdown调用后所有任务执行完成
  • 超时
  • 当前线程中断

例子

下面写一些实际例子展示上面的几个关闭方法

public class ThreadPoolShutdownTest {
   public static void main(String[] args){
       ThreadPoolExecutor service = (ThreadPoolExecutor)Executors.newFixedThreadPool(1);
       service.submit(new MyRunnable());
       service.submit(new MyRunnable2());
       service.shutdown();
       System.out.println(service.getQueue());
       System.out.println(service.isShutdown());
       System.out.println(service.isTerminated());
   }
   static class MyRunnable implements Runnable{
       public void run() {
           try{
               System.out.println("ThreadId:"+Thread.currentThread()+",开始执行");
               //这里模拟的是执行时间较长的任务
               TimeUnit.SECONDS.sleep(10);
               System.out.println("ThreadId:"+Thread.currentThread()+",执行完成");
           }catch (InterruptedException e){
               System.out.println("ThreadId:"+Thread.currentThread()+",中断");
           }
       }
   }
   static class MyRunnable2 implements Runnable{
       public void run() {
           //这里模拟的是执行时间短的任务
           System.out.println("ThreadId:"+Thread.currentThread()+",开始执行");
           System.out.println("ThreadId:"+Thread.currentThread()+",执行完成");
       }
   }
}

运行结果:

ThreadId:Thread[pool-1-thread-1,5,main],开始执行
[java.util.concurrent.FutureTask@23fc625e]
true
false
ThreadId:Thread[pool-1-thread-1,5,main],执行完成
ThreadId:Thread[pool-1-thread-1,5,main],开始执行
ThreadId:Thread[pool-1-thread-1,5,main],执行完成

在上述代码中我们使用了只有一个线程且无界队列,可以看到在我们调用shutdown,线程池队列里还有一个提交了但是等待执行的任务java.util.concurrent.FutureTask@23fc625e,我们的正在执行的任务和在等待队列中的任务还是会继续执行,isTerminated返回false也表明线程池在关闭的时候还有任务没执行完,那么这种方式的缺点是什么呢?

  • 当线程池关闭后,无论是已经在执行的任务还是提交了但未执行的任务还是会继续执行,如果某个任务执行时间很长或者阻塞了,这样会导致我们的应用程序无法关闭,有的时候这可能不是我们想要的结果
    将例子中的 service.shutdown();换成
List<Runnable> runnableList =service.shutdownNow();
        System.out.println(runnableList);

运行结果:

ThreadId:Thread[pool-1-thread-1,5,main],开始执行
ThreadId:Thread[pool-1-thread-1,5,main],中断
[java.util.concurrent.FutureTask@23fc625e]
[]
true
true

可以看到线程池中的队列里没有等待的任务了,这里isTerminated返回true,只要调用了shutdownNow,isTerminated一定返回true
那这种存在的问题是什么呢?可以看到在执行的任务被中断了,这可能也不是我们想要的结果

正确关闭

  • 首先任务要能够响应中断
  • 关闭流程:
  • 调用shutdown禁止提交新的任务
  • 调用 awaitTermination等待任务执行完成
  • 调用shutdownNow强制关闭那些执行任务过长(可能无法正常停止)的任务
long time_out=1*60 ;//超时时间,自己根据任务特点设置
       //第一步,调用shutdown等待在执行的任务和提交等待的任务执行,同时不允许提交任务
       service.shutdown();
       try {
           if(!service.awaitTermination(time_out, TimeUnit.SECONDS)){
               //如果等待一段时间后还有任务在执行中被中断或者有任务提交了未执行
               //1.正在执行被中断的任务需要编写任务代码的时候响应中断
               List<Runnable> waitToExecuteTaskList = service.shutdownNow();
               //2.处理提交了未执行的任务,一般情况不会出现
               for(Runnable runnable:waitToExecuteTaskList){
                   //todo
               }
           }
       }catch (InterruptedException e){//如果被中断了
           //1.正在执行被中断的任务需要编写任务代码的时候响应中断
           List<Runnable> waitToExecuteTaskList = service.shutdownNow();
           //2.处理提交了未执行的任务,一般情况不会出现
           for(Runnable runnable:waitToExecuteTaskList){
               //todo
           }
       }