addWorker() 方法
下面是增加了注释的addWorker方法的源码
/**
根据线程池当前状态和给定的边界条件(以核心线程数还是最大线程数为界)判断是否可以增加一个新的worker
(worker是线程池内部的一个对象,是thread对象的一个包装)。
如果可以增加一个新的worker,就响应的增加worker数量,并将传入的runnable作为worker的第一个任务执行。
如果线程池停了,或者正要关闭,或者线程工厂创建线程失败了,都会返回false。
线程创建失败的典型情况有内存溢出或线程工厂返回了Null
@param firstTask 参数是新创建线程执行的第一个任务。
Worker创建时候传入的firstTask有worker直接执行不进入任务队列。
@core 参数如果是true的话,以核心线程池大小为边界判断是否增加worker,否则以最大线程是大小作为边界。
**/
//addWoker代码可以分为两部分,上部分只是为了增加线程计数,如果增加失败,就返回了,下半部分是创建worker,启动线程
private boolean addWorker(Runnable firstTask, boolean core) {
//如果执行到了continue retry; ,retry 下面的循环会重复执行,
//如果执行到了 break retry; retry下面的循环会跳过,继续执行后面的代码
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);//rs 是线程池的运行状态
// Check if queue empty only if necessary.
/*左边的检查好理解,如果线程状态>=关闭,返回false,
右边的意思是如果传入的任务为空并且线程池已经关闭并且队列非空,那继续后面的代码,也就是要继续添加worker,
这是因为当线程池关闭的时候,有可能出现这种情况:
所有的线程都退出了,但是队列里有残留的任务,这时候就需要再起一个线程执行残留任务,
在我的 “java线程池(ThreadPoolExecutor)源码解析一” 的execute方法的 33行
addWorker(null, false); 就是用到了这个判断**/
if (rs >= SHUTDOWN &&! (rs == SHUTDOWN && firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
//CAPACITY 是能够创建的worker最大数
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS 增加Worker的计数,如果成功,继续后面的流程
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
//CAS 增加Worker的计数失败后,再重新判断线程池状态和最初状态是否一致,
//如果一致,只重复执行内层循环,如果不一致,从外层循环开始重新判断线程池状态
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//加锁后重现校验线程池状态,向workers中添加新的Worker实例
mainLock.lock();
try {
int c = ctl.get();
int rs = runStateOf(c);
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // 线程t必须没有启动过
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize) //largestPoolSize 用于记录线程池达到过的最大线程数
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
//启动线程失败后执行清理方法
addWorkerFailed(w);
}
return workerStarted;
}
为什么workers使用HashSet和ReentraintLock而不使用并发的set
源码注释中做了解释
/**
* 这个hashSet 包含线程池中所有的 worker。对其操作必须获得主锁(mainLock)
*/
private final HashSet<Worker> workers = new HashSet<Worker>();
/**
* mainLock 用来控制 wokers set 的访问和相关计数器。
* 通过过锁来控制一个 hashSet 的 workers 的操作
* 而不是使用某种类型的并发set(concurrent set)的主要原因是想避免在关闭线程池时
* 产生没有必要的线程中断风暴。否则那些还没有退出的线程将有可能被并发的调用interrupt。
* 这样也可以简化一些计数器的处理,比如在统计线程池曾达到过得最大线程数(largestPoolSize)。
*
*/
private final ReentrantLock mainLock = new ReentrantLock();