简介
线程池的概念,已经布满在众多的open source之中(jdk/tomcat/数据库连接driver/netty/zio/spring/akka/play/kafka/guava),用来尽可能的发挥计算机的多核处理能力,进而提高应用的效用,满足使用特殊的场景。此处仅就jdk ThreadPool进行说明
数据结构的分析
- jdk线程池,实际上是已有并发工具类的集合。
| 工具类 | 作用 |
|---|---|
| ReentrantLock mainLock | 确保数据修改能够正确进行 包括ctl workers |
| BlockingQueue workQueue | 线程池中,无法不能再创建新线程的时候,用来存放暂时来不及处理的Runnable |
| HashSet workers | 这里的workers,用来存放所有尚未完成的任务,每次修改数据,必须要与mainLock配合,才能保证安全 |
以上三者主要表达了--安全与数据存放
- 线程池用来表达自身相关的数据
- 该数据分作两类, 状态数据(status)/尚未完成的任务数(workerCount)
| 数据类型 | 说明 |
|---|---|
| 状态数据(status) | 规定了四种可能 RUNNING/SHUTDOWN/STOP/TIDYING/TERMINATED,不同的状态,定义了不同行为 |
| 尚未完成的任务数(workerCount) | 这个数据主要用于判断,小于线程池的可用线程时,创建新线程。否则放入到BlockQueue中 |
- 线程池用了一个int变量clt,来包括了这两类数据。 int数据有32bit.最高的3bit用作表示status,后29bit表示workerCount。 然后用|运算,将两个数据合并起来。 即xxx_yyyyyyyyyyyyyyyyyyyyyyyyyyyyy
- status的各种语义
| status | 二进制形式 | action semantic |
|---|---|---|
| RUNNING | 111_00000000000000000000000000000 高3bit 全是1 是唯一存在的负数 | 正常运行,同时此时clt是绝对小于0的,因而clt>=0时,说明线程池的status已不再是RUNNING |
| SHUTDOWN | 000_00000000000000000000000000000 高3bit 全是0 | 不再接受新的任务了,但依旧要处理queue中,剩余的任务 |
| STOP | 001_00000000000000000000000000000 | 不再接受新的任务了,同时也不再处理queue中,并打断所有在运行的任务 |
| TIDYING | 010_00000000000000000000000000000 | 所以任务已经结束,workerCount=0,然后去调用hook方法terminated(),进入最后的状态 |
| RUNNING | 011_00000000000000000000000000000 | 此时线程池已经终灭了 |
- 位运算之mask
| 位运算 | 说明 |
|---|---|
| status | 只计算前3bit,则需要高3bit都是1,其余是0的数据,clt进行&运算。即111_00000000000000000000000000000,也就是RUNNING |
| workerCount | 只计算后29bit,则需要高3bit都是0,其余是1的数据,clt进行&运算。即000_11111111111111111111111111111 |
令000_11111111111111111111111111111=(1<<29-1)=COUNT_MASK(意在获取workerCount) 则~COUNT_MASK=111_00000000000000000000000000000 可以计算status
以上就是基本的数据内容
On Worker
Worker的数据结构可以描述为Worker(firstTAsk,thread,state)
- firstTask
- Worker有一个指针firstTask. 代表他可以执行的第一个任务
- 在worker执行run方法的时候,调用了runWorker
- 第一个任务之后,就从workerQueue中去拉取task
- Woker实现了同步队列,要在借用同步队列的state,防止在worker在尚未开始的时候,被打断。同时保证自己的线程安全,独占模式。
- 构造器中
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
- 在runWorker的时候,worker先得让state归0。然后才能再去lock/unlock
- runWorker之末,finally 代码块,会处理worker的退出。前提是先退出while循环。
- 退出while循环条件 task=null 同时 getTask()=null。表明当前worker没有task可用,则应当回收当前worker.此processWorkerExit之所谓也。
3.processWorkerExit worker退出。出现异常的时候 completedAbruptly=true
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w); // 移除worker
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) { // 有RUNNING/SHUTDOWN两种可能 因为这两种状态才可以处理queue中任务
if (!completedAbruptly) {
// 核心线程是否存在时效 存在的时候 可以将所有的core thread都回收
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 核心线程有时效限制,同时workQueue还有任务
// 则值最少应该保留一个core thread继续执行 剩下任务
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 如果worker的数目 超过最小需求 则直接返回
if (workerCountOf(c) >= min)
return; // replacement not needed 针对的是 addWorker(null, false); 这个代码
}
//此时不满足 if (workerCountOf(c) >= min)这个条件completedAbruptly=true 即workerCount<min,需要对worker进行补足
addWorker(null, false);
}
}
- 根据以上的分析,可以得出的结论是,worker处于独占模式地,不断执行可以执行的任务,同时按照参数配置,考虑worker是否进行回收。
线程池的入口
线程池的入口方法,主要是提交任务。而实际上,要么创建worker/要么将任务暂时存入到blockQueue中/被拒绝。 以execute方法为例
| action | 说明 |
|---|---|
| addWorker | 不能超过规定的核心的线程资源。 |
| queue.offer | 超过规定的可用线程资源,先放入queue中 |
| rejected | 添加queue不成,同时有超过max可用核心线程资源,那便reject掉 |
addWorker
可以分为几个步骤
- 检查线程池状态,主体是一个循环
retry:
for (int c = ctl.get();;) {
// Check if queue empty only if necessary.
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty()))
return false; // 线程池状态不对
for (;;) {
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
return false; // worker数量超过了可用的线程资源
if (compareAndIncrementWorkerCount(c)) //修改ctl成功 跳出循环
break retry;
c = ctl.get(); // Re-read ctl
if (runStateAtLeast(c, SHUTDOWN))
continue retry; // 线程池不在RUNNING状态
// else CAS failed due to workerCount change; retry inner loop
}
}
- 线程安全添加worker
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
if (isRunning(c) ||
(runStateLessThan(c, STOP) && firstTask == null)) {
if (t.getState() != Thread.State.NEW)
throw new IllegalThreadStateException();
workers.add(w); // 添加新建的worker
workerAdded = true;
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
container.start(t); // 启动线程池创建的线程t
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); // 尚未启动 被认定为失败
}
return workerStarted;
}
getTask
主体也是循环 可以分为以下步骤
- 线程池状态检测
- 判断当前worker能否退出,被回收
- 拉取任务
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
// 第一步 检测状态
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
// 第二步 worker是否可以退出了
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
// 第三步 拉取任务
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
- 关于一个if条件的说明 worker是否可以退出
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
流程图大抵如此
从图中可以看出 1 当wc>maxPoolSize,但还是要确保退出后,至少还有一个worker,也就是说(wc>1),这时自然希望当前worker可以退出,以保持线程池定义的约束。 2 boolean timed => 核心线程支持超时退出(allowCoreThreadTimeOut)或者 wc>corePoolSize (此时核心线程不支持超时退出,但是可以退出核心线程以外的线程) 3 timedOut 间接表明了 workerQueue里面没有数据 拉取的任务是null 此时可以回收多余的worker
4 因此timed&&timeOut 表示worker可以进行回收,worker类型包括了核心和非核心