一、Java构建线程的方式
1.继承Thread(几乎不用)
2.实现Runnable
3.实现Callable
4.线程池(Java提供了构建线程池的方式)Java提供了Executors 常见线程池,(规范中不允许使用这种方式,对象成的控制力度比较低)
推荐手动创建
二、线程池的7个参数
三、线程池执行流程
四、线程池表示属性
状态之间的转换
五、线程池的execute方法(源码分析)
引用自https://zhuanlan.zhihu.com/p/398846689(源码分析比较详细)
// 初始化线程池的状态和当前线程数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 线程池的状态和数量分别由一个32位的整形前3位和后29位表示
// 这个COUNT_BITS=29
private static final int COUNT_BITS = Integer.SIZE - 3;
// COUNT_MASK二进制数值:0001 1111 1111 1111 1111 1111 1111 1111
// 29个1,基本上是用来做位运算计算线程数量的
// 还有一点就是,这个数代表最大线程数,因为29个位置都是1
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// 下面的5个状态,只需要关注二进制的前3位即可
// RUNNING = 1110 0000 0000 0000 0000 0000 0000 0000
// -1的话,需要转换成补码才能向右移29位
private static final int RUNNING = -1 << COUNT_BITS;
// SHUTDOWN = 0000 0000 0000 0000 0000 0000 0000 0000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// STOP = 0010 0000 0000 0000 0000 0000 0000 0000
private static final int STOP = 1 << COUNT_BITS;
// TIDYING = 0100 0000 0000 0000 0000 0000 0000 0000
private static final int TIDYING = 2 << COUNT_BITS;
// TERMINATED = 0110 0000 0000 0000 0000 0000 0000 0000
private static final int TERMINATED = 3 << COUNT_BITS;
// 计算c这个数值和1110 0000 0000 0000 0000 0000 0000 0000相与的结果,其实就是计算线程池的状态
private static int runStateOf(int c) { return c & ~COUNT_MASK; }
// 计算当前线程池的线程数量;c与0001 1111 1111 1111 1111 1111 1111 1111相与
private static int workerCountOf(int c) { return c & COUNT_MASK; }
// rs为线程状态,wc是线程数量;一般使用这个方法计算ctl值,也就是状态和数量共同值
private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池对象初始化时执行ctlOf(RUNNING,0)返回一个int值作为ctl的初始化值。RUNNING是前3位全部为1,与0执行位运算|,这个0就是初始化的时候是0个线程,得到的结果如下。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }
- 添加任务
public void execute(Runnable command) {
// 添加的任务不能为空
if (command == null)
throw new NullPointerException();
// ctl是一个32位的整形值,前3为代表线程池的状态,后29位代表当前线程池中线程的数量
int c = ctl.get();
// workerCountOf(c)这个方法是通过位运算计算当前线程池线程数量;当线程数量小于核心线程数,那么执行添加工作线程的操作
if (workerCountOf(c) < corePoolSize) {
// 创建新的工作线程;如果添加成功,直接返回;
if (addWorker(command, true))
return;
// 添加工作线程不成功,有可能是当前核心线程数已经满了;
//在多线程执行任务的时候,有可能判断的时候确实没有达到核心线程数,但是当真正添加的时候,前面已经有任务添加达到了核心线程数了
c = ctl.get();
}
// 这个时候,核心线程数已经满了;
// 如果线程池还在运行状态,那么就把这个任务添加到队列中去
if (isRunning(c) && workQueue.offer(command)) {
// 重新获取线程池状态
int recheck = ctl.get();
// 再确定一下线程池是否处于运行状态
// 如果线程池此刻没有处于运行状态,那么就把之前队列中的任务推出来
if (! isRunning(recheck) && remove(command))
// 把推出来的任务执行拒绝策略
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 上面如果添加到队列中也失败了,那么就创建非核心线程
else if (!addWorker(command, false))
// 如果添加非核心线程失败,那么执行拒绝策略
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 开启自旋;先获取线程池状态及数量
for (int c = ctl.get();;) {
// Check if queue empty only if necessary.
// 如果c=[SHUTDOWN | STOP | TIDYING | TERMINATED]其中之一,并且c = [STOP | TIDYING | TERMINATED] 或者 任务不为空 或者等待队列为空,就返回false
//翻译成人话,就是
// 1.如果线程池的状态处于[STOP | TIDYING | TERMINATED]中,那么就直接创建失败
// 2.如果线程池的状态处于SHUTDOWN,并且任务不为空的话,那么也创建失败。
// 3.如果线程池的状态处于SHUTDOWN,并且等待队列为空,同样创建失败。
// 上述条件下,满足其一,都会创建新线程失败,走向拒绝策略。
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty()))
return false;
// 走到这里,说明此刻线程池的状态处于RUNNING
for (;;) {
// 1.如果是创建核心线程,那么就比较一下当前线程数量是否大于等于核心线程数
// 2.如果是创建非核心线程,那么比较一下当前线程数是否大于等于最大线程数
// 如果上述条件确实满足,说明就不能创建工作线程了,那就要创建失败。
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
return false;
// 此时需要通过CAS的方式实现线程数+1,这个操作必须满足原子性。
if (compareAndIncrementWorkerCount(c))
// 如果自增成功,说明拿到了创建工作线程的通行证,那么就直接跳出双重循环,进入下一关去真正地创建工作线程。
break retry;
//没有拿到通行证的话,就好好地自旋
// 重新读取状态
c = ctl.get(); // Re-read ctl
// 如果当前状态处于[SHUTDOWN | STOP | TIDYING | TERMINATED]中,那么最终会返回false,创建工作线程失败
if (runStateAtLeast(c, SHUTDOWN))
continue retry;
// 否则重新内部自旋直至满足以下情况
// 1.线程数量超过限制,创建失败。
// 2.自增成功,拿到通行证,进入下一步创建工作线程
// 3.线程池状态发生变化,最终创建失败。
}
}
// 有通行证的才能执行到这里
// 下面新创建的工作线程是否开始运行的标识
boolean workerStarted = false;
// 下面新创建的工作线程是否被添加到工作线程集合中
boolean workerAdded = false;
Worker w = null;
try {
// 创建一个工作线程;
// 一个work绑定一个线程,创建work的同时,会创建一个对应的线程
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 获取一个重入锁
final ReentrantLock mainLock = this.mainLock;
// 上锁;
//如果看不明白的话,就把这两行代码替换成synchronized (this)
mainLock.lock();
// 解锁前的逻辑都不存在线程安全的问题
try {
// 重新检查线程池状态
int c = ctl.get();
// 1.如果线程池状态位RUNNUING
// 2.如果线程池状态处于[RUNNING | SHUTDOWN],并且任务为null的情况
// 那么都可以把当前工作线程放进工作线程集合中
if (isRunning(c) ||
(runStateLessThan(c,