JUC源码分析-ThreadPool-jdk7

无人久伴 提交于 2020-01-23 15:06:16

ThreadPool源码分析-jdk7

 

jdk7 并发包中的线程代码被重写。

线程池中原来代表线程数的poolSize 和代表状态的state被合并到ctl中

int类型共有32位,ctl的高三位用来表示状态,剩下的28位用来表示工作线程数

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

//可以代表工作线程数的比特位数

private static final int COUNT_BITS = Integer.SIZE - 3;

//工作线程的最大容量,28位可以表示最大的数值是 2^29-1

private static final int CAPACITY = (1 << COUNT_BITS) - 1;

 

// runState is stored in the high-order bits

//将线程池状态移动到高位 29~31位表示状态值

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

//获取线程池状态 c与上28位最大值的反码即可去除低28位

private static int runStateOf(int c) { return c & ~CAPACITY; }

//获取工作线程数 c与上28位最大值即可去除高4位

private static int workerCountOf(int c) { return c & CAPACITY; }

//拼接线程状态和工作线程数(线程池状态位于高4位,工作线程数位于低28位,或相当于拼接)

private static int ctlOf(int rs, int wc) { return rs | wc; }

 

//核心 逻辑方法

public void execute(Runnable command) {

if (command == null)

throw new NullPointerException();

int c = ctl.get();

//工作线程数小于核心线程数,启动工作线程执行任务

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);

//如果工作线程为0,启动一个新的工作线程

else if (workerCountOf(recheck) == 0)

addWorker(null, false);

}

//队列放满后,如果 工作线程数小于maximumPoolSize,启动新的工作线程,否则执行拒绝策略。

//如果此时执行shutdown,addWorker会返回false,然后执行拒绝策略

else if (!addWorker(command, false))

reject(command);

}

 

 

 

Worker-addWorker

private boolean addWorker(Runnable firstTask, boolean core) {

retry:

for (;;) {

int c = ctl.get();

int rs = runStateOf(c);

 

// Check if queue empty only if necessary.

//当调用shutdown后,阻止启动新线程(收尾工作线程除外)

//黄色部分是收尾线程的特征

if (rs >= SHUTDOWN &&

! (rs == SHUTDOWN &&

firstTask == null &&

! workQueue.isEmpty()))

return false;

for (;;) {//A

int wc = workerCountOf(c);

if (wc >= CAPACITY ||

wc >= (core ? corePoolSize : maximumPoolSize))

return false;

//如果成功跳出最外层循环

//这里的实现 没有使用 AtomicInteger.getAndAdd()系列方法,是因为 不是每一次增加工作线程数都必须成功,失败后要判断是否是线程池执行了shutdown()或超出线程数最大容量

if (compareAndIncrementWorkerCount(c))

break retry;

c = ctl.get(); // Re-read ctl

//两种情况 (1) 状态值已经被改变 ,需要从外层开始获取并判断状态

//(2)出现了ABA问题 黄色 A部分的循环 可以解决这个问题

if (runStateOf(c) != rs)

continue retry;

// else CAS failed due to workerCount change; retry inner loop

}

}

//执行任务的方法

final void runWorker(Worker w) {

Runnable task = w.firstTask;

w.firstTask = null;

boolean completedAbruptly = true;

try {

//注意:如果执行(task = getTask())==null,while循环不会执行

while (task != null || (task = getTask()) != null) {

w.lock();

clearInterruptsForTaskRun();

try {

//任务执行前的钩子

beforeExecute(w.thread, task);

Throwable thrown = null;

try {

//执行任务

task.run();

} catch (RuntimeException x) {

thrown = x; throw x;

} catch (Error x) {

thrown = x; throw x;

} catch (Throwable x) {

thrown = x; throw new Error(x);

} finally {//任务执行后的钩子

afterExecute(task, thrown);

}

} finally {

task = null;

w.completedTasks++;

w.unlock();

}

}

//执行到这的不是 keepAlive导致的timeout,就是shutdown

. completedAbruptly = false;

} finally {

执行wokert退出

processWorkerExit(w, completedAbruptly);

}

}

//如果不是stop状态 就要清除打断标识,保证当前线程后续阻塞执行不会被打断

//如果是stop状态 就要保存打断状态,保证当前线程后续执行会被打断

private void clearInterruptsForTaskRun() {

if (runStateLessThan(ctl.get(), STOP) &&//c < s

Thread.interrupted() &&//返回是否被打断

runStateAtLeast(ctl.get(), STOP))//c >= s

Thread.currentThread().interrupt();

}

clearInterruptsForTaskRun中这种写法如下

假设 代码段 A,B,C返回值都是boolean类型,有以下逻辑

if(A){

B

}

else if(C)

{

D

}

可以写成 if(A&&B&&C){ D},我个人认为 这样写 的优点是简洁,但是易读性差,不知是否这样写性能会好些,JDK的代码很多是这样写的。

 

下面分析一下getTask,这里面正常队列任务的获取,也包含了keepAliveTime的执行处理。

//结合runWorker的分析,得出方法作用:获取队列中的任务,如果返回null,当前线程会自我终止,并移出工作线程。

private Runnable getTask() {

boolean timedOut = false; // Did the last poll() time out?

 

retry:

for (;;) {

int c = ctl.get();

int rs = runStateOf(c);

 

// Check if queue empty only if necessary.

//检查队列时否为空

if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {

decrementWorkerCount();

return null;

}

//工作线程是否需要超时销毁

boolean timed; // Are workers subject to culling?

 

for (;;) {

int wc = workerCountOf(c);

//当允许核心线程超时销毁或工作线程数超过核心线程数时,标识当前线程需要超时销毁。

timed = allowCoreThreadTimeOut || wc > corePoolSize;

//条件A:wc <= maximumPoolSize 工作线程数小于最大线程数, A 成立的情况下 !(timedOut && timed) 只要timedOut ,timed有一个是false ,这个表达式就是true.

//正常情况下 条件A满足, timedOut && timed==false, 执行break 从队列中获取数据。

//满足条件A后 , 当 timedOut && timed==true:,标识当前线程需要超时销毁并出现了timeOut的情况。需要跳过break,执行线程销毁。

if (wc <= maximumPoolSize && ! (timedOut && timed))

break;

//减少工作线程线程数,成功返回null

if (compareAndDecrementWorkerCount(c))

return null;

//上步骤可能失败,重新获取C

c = ctl.get(); // Re-read ctl

//线程池状态改变,从头开始检查状态,有可能执行了shutdown

if (runStateOf(c) != rs)

continue retry;

//CAS失败, 其他线程导致workerCount改变,重试内部循环

// else CAS failed due to workerCount change; retry inner loop

}

 

try {

//timed==false 标识工作线程不需要超时销毁 执行take

//timed==true 标识工作线程需要超时销毁 执行poll

//keepAliveTime 在初始化对象时由用户传参转换成纳秒。

Runnable r = timed ?

workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

workQueue.take();

if (r != null)

return r;

//关键,当poll执行并timeout后,执行到这。

timedOut = true;

} catch (InterruptedException retry) {

timedOut = false;

}

}

}

getTask主要任务

  1. 检查线程池状态,如果状态为SHUTDOWN且队列位空,或状态为stop返回null.
  2. 检查工作线程是需要销毁,如果是返回null
  3. 获取队列中的任务,take获取或 poll timeout获取。

需要注意 keepAliveTime的执行逻辑, 当工作线程数超过核心线程数(allowCoreThreadTimeOut 默认为false)时,调用 queue.poll timeout方法。 如果执行超时,标识timeout,下一轮循环 检查到 了超时,则执行任务销毁处理。

 

//执行工作线程退出处理。

private void processWorkerExit(Worker w, boolean completedAbruptly) {

if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted

decrementWorkerCount();

 

final ReentrantLock mainLock = this.mainLock;

//统计数据,移除worker缓存

mainLock.lock();

try {

completedTaskCount += w.completedTasks;

workers.remove(w);

} finally {

mainLock.unlock();

}

 

tryTerminate();

 

int c = ctl.get();

if (runStateLessThan(c, STOP)) {

if (!completedAbruptly) {// 不是突然完成

int min = allowCoreThreadTimeOut ? 0 : corePoolSize;

if (min == 0 && ! workQueue.isEmpty())

min = 1;

if (workerCountOf(c) >= min)

//允许核心工作线程超时处理时且存活的工作线程大于1个或不允许核心工作线程超时处理时且存活的工作线程大于核心线程数 不需要启动收尾线程。

return; // replacement not needed

}

//启动一个收尾线程

addWorker(null, false);

}

}

 

shutdown的几种情况

task队列不为空

 

重写后的线程池有以下个改动点

  1. 代表线程数的poolSize 和代表状态的state被合并到ctl中
  2. 1.6版本的shutdown 执行后 getTask调用queue.pull()(不是timeout方法), 相同的情况1.7版本 的getTask调用take,获取任务后要复原 interrupt的状态,还要在tryTerminate中 执行 打断一个worker.这样处理 复杂了许多
  3. 锁粒度减小, 合并了一些方法。

why do this

第1个改动点 反复查看代码 优化了 锁粒度。没看到其他的优化

加入了很多for循环和判断 可读性变差

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!