线程池的shuwdown()和shutdownNow()和其他方法

本文只是总结结论,至于方法的底层代码原理,想要深入了解的话请再去百度.

线程池运行状态介绍

ThreadPoolExecutor 使用 runState (运行状态)这个变量对线程池的生命周期进行控制,线程池关闭过程会有频繁的运行状态转化,所以我们首先需要了解线程池的各种运行状态及其之间的转化关系,runState 一共有以下5种取值:

  • RUNNING:接收新的任务并对任务队列里的任务进行处理;
  • SHUTDOWN:不再接收新的任务,但是会对任务队列中的任务进行处理;
  • STOP:不接收新任务,也不再对任务队列中的任务进行处理,并中断正在处理的任务;
  • TIDYING:所有任务都已终止,线程数为0,在转向TIDYING状态的过程中,线程会执行terminated()钩子方法,钩子方法是指在本类中是空方法,而在子类中进行具体实现的方法;
  • TERMINATED:terminated()方法执行结束后会进入这一状态,表示线程池已关闭。

线程池运行状态存储在AtomicInteger类型的变量ctl的最高三位中,因此各种状态所对应的整型变量的二进制格式除了最高三位,其余都是0 (如代码注释所示)。这些变量的操作会涉及到一些位运算和原子操作,现在只需要了解这些状态变量从小到大的顺序是RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED,另外很重要的一点就是这些状态的转化只能从小到大,不能从大到小。它们在源码中对应的变量如下:

// RUNNING: 十进制:-536870912  二进制:11100000000000000000000000000000
private static final int RUNNING    = -1 << COUNT_BITS; 
// SHUTDOWN: 十进制:0  二进制:0
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// STOP: 十进制:536870912  二进制:00100000000000000000000000000000
private static final int STOP       =  1 << COUNT_BITS;
// TIDYING: 十进制:1073741824  二进制:01000000000000000000000000000000
private static final int TIDYING    =  2 << COUNT_BITS;
// TERMINATED: 十进制:1610612736  二进制:01100000000000000000000000000000
private static final int TERMINATED =  3 << COUNT_BITS;

// COUNT_BITS: 29
private static final int COUNT_BITS = Integer.SIZE - 3;

运行状态的转化条件和转化关系如下所示:

线程池的shuwdown()和shutdownNow()和其他方法_第1张图片

线程池运行状态转化关系图

一.Shutdown()

1.当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则会报RejectedExecutionException,并直接结束main方法.

2.此时线程池不会立刻关闭,直到线程池中所有的任务都已经处理完成,才会关闭。

(线程池中所有的任务指的是 线程池中正在运行的任务+还在队列中的任务)

3.此方法对于线程是阻塞状态(sleep/wait等)还是非阻塞状态没有任何影响,线程该怎么运行还是怎么运行.

4.Shutdown()没有返回值

此方法不会阻塞main线程.

二.shutdownNow()

1.当线程池调用该方法时,线程池的状态则立刻变成STOP状态。

2.继续运行正在运行的任务.

3.不再运行任务队列中的任务。然后会将任务队列中正在等待的所有任务转移到一个 List 中,形成一个list任务列表并返回。

返回的任务列表list长下面那样:

[com.example.wordtopdf.MyBRunnable@4e50df2e, com.example.wordtopdf.MyBRunnable@1d81eb93, com.example.wordtopdf.MyBRunnable@7291c18f, com.example.wordtopdf.MyBRunnable@34a245ab]

我们可以根据返回的任务 List 来进行一些补救的操作,例如记录在案并在后期重试

3.不接收新任务。如果执行完shutdownNow()后有新任务提交,会报RejectedExecutionException,并直接结束main方法.

4.shutdownNow 会给线程池中的每个线程发送 interrupt 中断信号,相当于让线程池中的每个线程自己都调了一遍 interrupt()方法.

有了 interrupt 中断信号后,当线程后面再去运行 sleep,同步锁的 wait,socket 中的 receiver,accept 等阻塞方法时,线程就会抛出 InterruptException 异常,并清除掉线程的中断标识。

因此只给interrupt 中断信号并不能直接让线程结束,只有在遇到下面类似的代码时,线程才会结束

@Override
public void run() {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
        return;//直接return,结束run方法,此线程也就运行完了,就会被结束掉.
    }
}

此方法不会阻塞main线程.

三.awaitTermination()

boolean b = 线程池对象.awaitTermination(3, TimeUnit.SECONDS);

第一个参数指定的是时间,第二个参数指定的是时间单位(当前是秒)。返回值类型为boolean型。 

 awaitTermination()方法,它本身并不是用来关闭线程池的,而是主要用来判断线程池状态的。

比如我们给 awaitTermination 方法传入的参数是 10 秒,那么它就会陷入 10 秒钟的等待,直到发生以下三种情况之一:

1.等待期间(包括进入等待状态之前)线程池已关闭并且所有已提交的任务(包括正在执行的和队列中等待的)都执行完毕,相当于线程池已经“终结”了,方法便会返回 true

2.等待超时时间到后,第一种线程池“终结”的情况始终未发生,方法返回 false

此方法会阻塞main线程.

四.isShutdown

 isShutdown(),它可以返回 true 或者 false 来判断线程池是否已经开始了关闭工作,也就是是否执行了 shutdown 或者 shutdownNow 方法。

此方法不会阻塞main线程.

五.isTerminated

当线程池中的所有任务处理完了,线程都关闭了,线程池彻底关闭了,才会返回true,否则都返回false.

此方法不会阻塞main线程.

你可能感兴趣的:(多线程/jvm/计算机,java,jvm,开发语言)