一、基础概述
通过源码我们知道线程池ThreadPoolExecutor内部是通过AtomicInteger类型的 ctl变量来控制运行状态和线程个数;在通过AtomicInteger源码知道器内部就是维护了一个int类型的value值,如下
private volatile int value;
那么通过一个值如何维护状态和个数两个值呢?那么接下来通过底层源码来看大神Doug Lea是如何设计的。
二、源码分析
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 【该段注释已经说明了设计思想,可以好好的看下】
* 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
*
* 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.
*
* The workerCount is the number of workers that have been
* permitted to start and not permitted to stop. The value may be
* transiently different from the actual number of live threads,
* for example when a ThreadFactory fails to create a thread when
* asked, and when exiting threads are still performing
* bookkeeping before terminating. The user-visible pool size is
* reported as the current size of the workers set.
*
* The runState provides the main lifecycle control, taking on values:
*
* 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
*
* Threads waiting in awaitTermination() will return when the
* state reaches TERMINATED.
*
* Detecting the transition from SHUTDOWN to TIDYING is less
* straightforward than you'd like because the queue may become
* empty after non-empty and vice versa during SHUTDOWN state, but
* we can only terminate if, after seeing that it is empty, we see
* that workerCount is 0 (which sometimes entails a recheck -- see
* below).
*/
// 初始化ctl值为0,并设置状态为RUNNING
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// Integer.SIZE = 32,即取29位作为计数
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 取高三位表示以下运行时的状态
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
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; }
// 计算clt个数
private static int ctlOf(int rs, int wc) { return rs | wc; }
}
通过以上我们知道线程池维护了五种状态,分别为:
RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED
且根据 AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)) 可以判断初始状态为RUNNING状态。
一、clt状态和数值布局分析
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 取高三位表示以下运行时的状态
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
/**
* 各个值的二进制表示:
*
* 1111 1111 1111 1111 1111 1111 1111 1111 (-1)
* 0000 0000 0000 0000 0000 0000 0000 0000 (0)
* 0000 0000 0000 0000 0000 0000 0000 0001 (1)
* 0000 0000 0000 0000 0000 0000 0000 0010 (2)
* 0000 0000 0000 0000 0000 0000 0000 0011 (3)
*
* 【分析】:
* 初始容量值,高三位全是0,低29位全是1;后续操作会以此为基础进行操作
* CAPACITY 000 1 1111 1111 1111 1111 1111 1111 1111
*
* ---------------3位-1位 -28位---
* 【前三位,表明状态位,后29位表明线程个数,即 2^29 - 1 基本够用了】
*
* RUNNING(-536870912) 111 0 0000 0000 0000 0000 0000 0000 0000
* SHUTDOWN(0) 000 0 0000 0000 0000 0000 0000 0000 0000
* STOP(536870912) 001 0 0000 0000 0000 0000 0000 0000 0000
* TIDYING(1073741824) 010 0 0000 0000 0000 0000 0000 0000 0000
* TERMINATED(1610612736) 011 0 0000 0000 0000 0000 0000 0000 0000
*
*/
状态 | 源码表示
A << X 表示 A二进制表示向左移动X位 COUNT_BITS = 29
| 线程池 状态位 | 工作线程个数 | |
前3位 | 后29位 | |||
CAPACITY | (1 << COUNT_BITS) - 1 | 000 | 1 | 1111 1111 1111 1111 1111 1111 1111 |
RUNNING | -1 << COUNT_BITS | 111 | 0 | 0000 0000 0000 0000 0000 0000 0000 |
SHUTDOWN | 0 << COUNT_BITS | 000 | 0 | 0000 0000 0000 0000 0000 0000 0000 |
STOP | 1 << COUNT_BITS | 001 | 0 | 0000 0000 0000 0000 0000 0000 0000 |
TIDYING | 2 << COUNT_BITS | 010 | 0 | 0000 0000 0000 0000 0000 0000 0000 |
TERMINATED | 3 << COUNT_BITS | 011 | 0 | 0000 0000 0000 0000 0000 0000 0000 |
根据以上分析得:
RUNNING -- 对应的高3位值是111。
SHUTDOWN -- 对应的高3位值是000。
STOP -- 对应的高3位值是001。
TIDYING -- 对应的高3位值是010。
TERMINATED -- 对应的高3位值是011。
二、基本方法分析
1) 状态方法
// 判断当前运行状态
private static int runStateOf(int c) {
return c & ~CAPACITY;
}
根据方法,我们知道CAPACITY取反后与c进行&操作,得到状态位
分析:
CAPACITY 000 1 1111 1111 1111 1111 1111 1111 1111
先取反操作得到
CAPACITY 111 0 0000 0000 0000 0000 0000 0000 0000
我们可以看到,此时高三位已经变成了1,低29位全部是0,那么我们与c进行&操作则可以得到高三位的值,从而获取到线程池的状态位。
核心:将CAPACITY低29位取0,高3位取1,然后和c进行&操作即可,保证和设计上一致。
2) 工作线程个数方法
// 工作线程数
private static int workerCountOf(int c) {
return c & CAPACITY;
}
分析:
假如c=0,即初始化状态,ctl值为默认值0;
那么我们如何计算才能计算出工作线程数呢(此时工作线程数为0)?
我们知道低29位用来存工作线程个数,那么此时的CAPACITY值如下,高3位0,低29位全是1,那么要计算当前clt的工作线程个数,则只需要将clt的值 和 CAPACITY 进行&操作即可。
CAPACITY 000 1 1111 1111 1111 1111 1111 1111 1111
&
c 000 0 0000 0000 0000 0000 0000 0000 0000
得到结果为0,即当前工作线程个数为0。
核心:将CAPACITY高3位取0,低29位取1,然后和c进行&操作即可,保证和设计上一致。
3) clt值计算方法
// rs 运行状态
// wc 工作线程个数
private static int ctlOf(int rs, int wc) {
// 两者进行或(|)操作,还原高3位和低29位的所有值,结果即ctl值
return rs | wc;
}
// 如下方法应用,先通过ctlOf(TIDYING, 0)方法计算ctl值,然后在通过compareAndSet设置最新的ctl值。
ctl.compareAndSet(c, ctlOf(TIDYING, 0))
核心:将CAPACITY高3位,低29位,进行或(|)操作即可还原ctl,保证和设计上一致。
不得不说,大佬设计的东西太特么完美了.
三、各种线程池状态的任务处理说明
RUNNING:处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。
SHUTDOWN:处于SHUTDOWN状态的线程池不可以接受新任务,但是可以对已添加的任务进行处理。
STOP:处于STOP状态的线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
TIDYING:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
TERMINATED:线程池彻底终止的状态。
总结:
以上回答了 线程池(一、基础理论)中的2个问题
- 线程池中运行状态和工作线程个数的设计原理
- 各种运行状态对任务的处理
参考链接: