上一篇分析线程池的创建过程,那么本篇就要学习下线程池如何处理任务。本篇涉及到大量的源码分析,所以可能会有些枯燥,但我会尽量化繁为简,希望能够通俗易懂。
1.线程池状态介绍
在学习线程池如何处理提交的任务之前,有必要了解下线程池的运行状态。我们应该知道一个线程的状态分为创建(NEW)、运行(RUNNABLE)、阻塞(BLOCK)、等待(WAITING/TIMED-WAITING)和结束(TERMINATED),线程是线程池的一部分,线程的状态和线程数量决定着线程池的状态,Java把线程池的状态分为五种:
- 运行(RUNNING):该状态下的线程池接收新任务并处理队列中的任务;线程池创建完毕就处于该状态,也就是正常状态;
- 关机(SHUTDOWN):线程池不接受新任务,但处理队列中的任务;线程池调用shutdown()之后的池状态;
- 停止(STOP):线程池不接受新任务,也不处理队列中的任务,并中断正在执行的任务;线程池调用shutdownNow()之后的池状态;
- 清理(TIDYING):线程池所有任务已经终止,workCount(当前线程数)为0;过渡到清理状态的线程将运行terminated()钩子方法;
- 终止(TERMINATED):terminated()方法结束后的线程池状态;
线程池的五种状态,从侧面也反应出线程池的生命周期,线程池某刻的状态用一个AtomicInteger变量ctl来表示,变量ctl可以说明线程池的两个属性:工作线程数(workerCount)和运行状态(runState);线程池一旦创建则workerCount默认为0,runState为RUNNING。
此时你可能会有疑问:如何用一个AtomicInteger变量表示两个属性以及为什么要用AtomicInteger?
下面先说说AtomicInteger变量如何表示两个属性。
AtomicInteger是int类型的原子包装类,最大的特点就是线程安全,所以其本质上还是个int值;那么int类型是4个字节,共有32位;Java利用位来表示池的两个属性。用int类型的高3位表示线程池的状态,低29位表示工作线程的个数,所以workerCount最大值为2^29-1。
private static final int COUNT_BITS = Integer.SIZE - 3;
// 000-1111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 111 - 0000000000000000000000000000(十进制: -536, 870, 912)
private static final int RUNNING = -1 << COUNT_BITS;
// 000 - 0000000000000000000000000(十进制: 0)
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 001 - 00000000000000000000000000(十进制: 536,870, 912)
private static final int STOP = 1 << COUNT_BITS;
// 010 - 00000000000000000000000000.(十进制值: 1, 073, 741, 824)
private static final int TIDYING = 2 << COUNT_BITS;
// 101 - 000000000000000000000000000(十进制值: 1, 610,612, 736)
private static final int T