本文只是总结结论,至于方法的底层代码原理,想要深入了解的话请再去百度.
ThreadPoolExecutor 使用 runState (运行状态)这个变量对线程池的生命周期进行控制,线程池关闭过程会有频繁的运行状态转化,所以我们首先需要了解线程池的各种运行状态及其之间的转化关系,runState 一共有以下5种取值:
TIDYING
状态的过程中,线程会执行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;
运行状态的转化条件和转化关系如下所示:
线程池运行状态转化关系图
1.当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则会报RejectedExecutionException,并直接结束main方法.
2.此时线程池不会立刻关闭,直到线程池中所有的任务都已经处理完成,才会关闭。
(线程池中所有的任务指的是 线程池中正在运行的任务+还在队列中的任务)
3.此方法对于线程是阻塞状态(sleep/wait等)还是非阻塞状态没有任何影响,线程该怎么运行还是怎么运行.
4.Shutdown()没有返回值
此方法不会阻塞main线程.
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线程.
boolean b = 线程池对象.awaitTermination(3, TimeUnit.SECONDS);
第一个参数指定的是时间,第二个参数指定的是时间单位(当前是秒)。返回值类型为boolean型。
awaitTermination()方法,它本身并不是用来关闭线程池的,而是主要用来判断线程池状态的。
比如我们给 awaitTermination 方法传入的参数是 10 秒,那么它就会陷入 10 秒钟的等待,直到发生以下三种情况之一:
1.等待期间(包括进入等待状态之前)线程池已关闭并且所有已提交的任务(包括正在执行的和队列中等待的)都执行完毕,相当于线程池已经“终结”了,方法便会返回 true
2.等待超时时间到后,第一种线程池“终结”的情况始终未发生,方法返回 false
此方法会阻塞main线程.
isShutdown(),它可以返回 true 或者 false 来判断线程池是否已经开始了关闭工作,也就是是否执行了 shutdown 或者 shutdownNow 方法。
此方法不会阻塞main线程.
当线程池中的所有任务处理完了,线程都关闭了,线程池彻底关闭了,才会返回true,否则都返回false.
此方法不会阻塞main线程.