状态定义
线程池ThreadPoolExecutor有两种属性:运行状态和线程数。
线程数很好理解,就是池子中有多少线程;运行状态共有5种,分别是
RUNNING:运行中, 为初始状态,即刚创建的线程池就是此状态。
SHUTDOWN:停工状态,不再接收新任务,但会继续处理队列中的任务。
STOP:停止状态,不再接收新任务,不再处理已有任务,且会中断正在执行的任务。
TIDYING:清理中,所有任务都停止了,且线程数量也降为0。
TERMINATED,终止状态,钩子函数terminated()已经执行完成,线程池彻底销毁。
复制代码
状态流转
五种状态的流转图如下
源码注释写的非常棒,此处贴一下
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
* When both queue and pool are empty
* STOP -> TIDYING
* When pool is empty
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
复制代码
代码实现
线程数是一个非负整数,可以用一个int来表示。
运行状态固定只有5种,为节省空间,可以用三个bit来表示,因为三个bit最多可以表示8种状态了。
线程池的这两个属性是需要同时访问的,也就是需要保证原子操作,如果按上面分开表示的话,修改时估计还得加一把锁。
那有没有更优雅的实现呢,我们来看一下Doug Lea大牛的实现吧
/**
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 表示线程数的bit数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池支持的最大的线程数量(536870911,完全够用了)
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits(运行状态保存在int的高三位)
// 通过注释的数字可以看出,运行状态对应的数字依次增大,这很重要,方便后续的运行状态判断
// 1110 0000 0000 0000 0000 0000 0000 0000(-536870912)
private static final int RUNNING = -1 << COUNT_BITS;
// 0000 0000 0000 0000 0000 0000 0000 0000(0)
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 0010 0000 0000 0000 0000 0000 0000 0000(536870912)
private static final int STOP = 1 << COUNT_BITS;
// 0100 0000 0000 0000 0000 0000 0000 0000(1073741824)
private static final int TIDYING = 2 << COUNT_BITS;
// 0110 0000 0000 0000 0000 0000 0000 0000(1610612736)
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl,全是原子操作
// 获取运行状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 获取线程数量
private static int workerCountOf(int c) { return c & CAPACITY; }
// 组装运行状态和线程数量,成为ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
/*
* 因为运行状态存在int的高三位,且依次增大,因此可以巧妙的通过大小比较得知两状态关系
*/
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
// 判断线程是否在运行
// 此处为啥用 < SHUTDOWN 而不是直接 = RUNNING 呢,因为ctl还有低29位表示的线程数呢
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
复制代码
Doug Lea巧妙的使用了位运算,用一个AtomicInteger解决了操作两个属性的原子性问题;同时把状态放在高三位,还可以方便的通过大小比较来的值线程池的状态。
关于为什么使用int而不是long,Doug Lea也很贴心的给出了解释
* In order to pack them into one int, we limit workerCount to
* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
* billion) otherwise representable. If this is ever an issue in
* the future, the variable can be changed to be an AtomicLong,
* and the shift/mask constants below adjusted. But until the need
* arises, this code is a bit faster and simpler using an int.
复制代码
用29位表示线程数,已经可以支持5亿个线程了,一般我们会通过-Xss1024K来设置线程栈大小为1M,如果我们达到5亿个线程,光线程栈空间就需要占用至少500T的内存了,真的难以想象我们有一天能到达这个量级。
不过Doug Lea还是很严谨的考虑了这种可能,如果AtomicInteger不够用了,就改用AtomicLong,不过目前来说用AtomicInteger更快更简单,不过度设计,够用就好~