目录
1、registerWorker / deregisterWorker
3、shutdown / shutdownNow / awaitTermination
4、awaitQuiescence / quiesceCommonPool
5、awaitJoin / helpComplete / externalHelpComplete
6、hasQueuedSubmissions / getQueuedSubmissionCount / getQueuedTaskCount
本篇博客讲解ForkJoinPool中线程创建与销毁,执行任务,线程池关闭等功能的实现细节
1、registerWorker / deregisterWorker
registerWorker是在ForkJoinWorkerThread的构造方法中调用的,通知线程池新增了一个Worker,会配套的创建一个关联的WorkQueue,将其保存在WorkQueue数组中,保存的位置是根据indexSeed计算出来的,是一个奇数,如果对应位置的WorkQueue非空,则遍历WorkQueue数组找到一个WorkQueue为空且索引是奇数的位置。注意新创建的WorkQueue的模式跟线程池的模式一致,FIFO或者LIFO,且scanState初始就是激活状态,其值等于该WorkQueue数组中的位置。
deregisterWorker是在ForkJoinWorkerThread执行任务的过程中出现未捕获异常导致线程退出时或者创建Worker线程异常时调用的,会将关联的WorkQueue标记为已终止,将其steals属性累加到线程池的stealCounter属性中,取消掉所有未处理的任务,最后从WorkQueue数组中删除,将AC和TC都减1,如果有空闲的Worker线程则唤醒一个,如果没有且当前线程数小于parallelism则创建一个新线程,其调用链如下:
这两方法的实现如下:
//通知线程池新增一个WorkerThread
final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
UncaughtExceptionHandler handler;
//设置Daemon为true
wt.setDaemon(true); // configure thread
if ((handler = ueh) != null)
//保存异常处理器
wt.setUncaughtExceptionHandler(handler);
WorkQueue w = new WorkQueue(this, wt);
int i = 0; // assign a pool index
//获取线程池的队列模式
int mode = config & MODE_MASK;
//尝试获取锁,如果获取失败则阻塞当前线程
int rs = lockRunState();
try {
WorkQueue[] ws; int n; // skip if no array
if ((ws = workQueues) != null && (n = ws.length) > 0) {
//如果workQueues已初始化,indexSeed初始值为0
int s = indexSeed += SEED_INCREMENT; // unlikely to collide
int m = n - 1;
//算出来的i肯定是一个奇数
i = ((s << 1) | 1) & m; // odd-numbered indices
if (ws[i] != null) { //如果对应索引的WorkQueue非空
int probes = 0; // step by approx half n
int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
//遍历数组找到一个WorkQueue为空的索引,因为step是一个偶数,i原来是一个奇数,所以最终i还是一个奇数
while (ws[i = (i + step) & m] != null) {
if (++probes >= n) {
//原数组遍历了一遍,没有找到空闲的数组元素,则将其扩容一倍
//将原数组的元素复制到新数组中
workQueues = ws = Arrays.copyOf(ws, n <<= 1);
m = n - 1;
probes = 0;
}
}
}
//保存w
w.hint = s; // use as random seed
//低16位保存w在WorkQueue数组中的位置,跟线程池的模式一致
w.config = i | mode;
w.scanState = i; //初始就是激活状态
ws[i] = w;
}
} finally {
//解锁,如果runState发生改变,则唤醒所有等待获取锁的线程
unlockRunState(rs, rs & ~RSLOCK);
}
//设置线程名
wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
return w;
}
//通知线程池某个WorkerThread即将退出
final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
WorkQueue w = null;
if (wt != null && (w = wt.workQueue) != null) {
WorkQueue[] ws; // remove index from array
//获取在数组中的位置
int idx = w.config & SMASK;
//加锁
int rs = lockRunState();
if ((ws = workQueues) != null && ws.length > idx && ws[idx] == w)
//ws不为空且对应索引的元素是目标WorkQueue w,将其置为null
ws[idx] = null;
//解锁
unlockRunState(rs, rs & ~RSLOCK);
}
long c; // decrement counts
//将AC和TC都原子的减1,CAS失败则重试
do {} while (!U.compareAndSwapLong
(this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) |//获取最高的16位
(TC_MASK & (c - TC_UNIT)) |//获取紧挨着的16位
(SP_MASK & c)))); //获取低32位
if (w != null) {
w.qlock = -1; //表明该WorkQueue已终止工作
w.transferStealCount(this); //将该WorkQueuen的steals累加到线程池的stealCounter属性中
w.cancelAll(); //取消掉所有未完成的任务
}
for (;;) { // possibly replace
WorkQueue[] ws; int m, sp;
if (tryTerminate(false, false) || w == null || w.array == null ||
(runState & STOP) != 0 || (ws = workQueues) == null ||
(m = ws.length - 1) < 0) //如果线程池正在终止或者已经终止了
break;
if ((sp = (int)(c = ctl)) != 0) { //唤醒空闲的Worker线程
if (tryRelease(c, ws[sp & m], AC_UNIT))
break;
}
//没有空闲的线程
else if (ex != null && (c & ADD_WORKER) != 0L) { //如果是异常退出且线程总数小于parallelism
tryAddWorker(c); //增加一个新线程
break;
}
else //不需要替换
break;
}
if (ex == null) //清空所有异常
ForkJoinTask.helpExpungeStaleExceptions();
else //重新抛出异常
ForkJoinTask.rethrow(ex);
}
//取消掉所有未执行完成的任务
final void cancelAll() {
ForkJoinTask<?> t;
if ((t = currentJoin) != null) {
currentJoin = null;
ForkJoinTask.cancelIgnoringExceptions(t);
}
if ((t = currentSteal) != null) {
currentSteal = null;
ForkJoinTask.cancelIgnoringExceptions(t);
}
while ((t = poll()) != null)
ForkJoinTask.cancelIgnoringExceptions(t);
}
2、runWorker
runWorker是ForkJoinWorkerThread的run方法调用的,该方法是Worker线程执行任务的核心实现。该方法首先调用scan方法,scan方法会随机的从某个WorkQueue中获取未执行的任务,如果该WorkQueue为null或者没有未执行的任务,则继续遍历下一个WorkQueue,如果所有WorkQueue都遍历了,且遍历了三遍还是没有获取待执行的任务,且这期间没有新增的任务提交任何一个WorkQueue中,则会将该WorkQueue标记为INACTIVE并将AC减1,然后返回null,进入awaitWork方法。如果成功获取待执行的任务就调用runTask方法执行该任务,注意该方法不仅仅是执行该任务,还会将该WorkQueue中未执行的任务都执行完了,执行的过程中会将scanState的SCANNING标识去除,等所有任务都执行完成了再加上标识SCANNING。scan方法涉及WorkQueue中的两个关键属性,scanState和stackPred,某个Worker线程刚创建时其关联的WorkQueue的scanState就是该WorkQueue在数组中的索引,是一个非负数,参考registerWorker方法。当Worker线程执行scan方法无法获取到待执行的任务,会将关联的WorkQueue标记成INACTIVE,scanState属性的最高位变成1,其值变成一个负数,然后通过stackPred保存原来ctl属性的低32位的值,将变成负数的scanState写入ctl的低32位中并且将ctl中AC减1。当signalWork方法唤醒空闲的Worker线程时,会根据ctl属性的低32位获取Worker线程关联的WorkQueue,将其scanState的INACTIVE标识去除,scanState变成一个非负数,将AC加1,将stackPred属性写入ctl的低32位中,即将ctl恢复到该WorkQueue被置为INACTIVE时的状态。综上,ctl的低32位中保存着最近被置为INACTIVE的WorkQueue的索引,而该WorkQueue由保存着之前的ctl的低32位的值,据此可以不断向上遍历获取所有被置为INACTIVE状态的WorkQueue。可参考stackPred属性的调用链,如下:
进入awaitWork方法,如果线程池准备关闭或者当前线程等待被激活超时,则返回false,终止外层的for循环,Worker线程退出,否则将当前线程阻塞指定的时间或者无期限阻塞,直到signalWork方法或者tryRelease方法将其唤醒,可参考parker属性的调用链,如下:
参考awaitWork方法的实现可知,如果有多个Worker线程关联的WorkQueue依次进入此逻辑,则只有最后一个进入此逻辑的线程因为等待激活超时而退出,该线程退出后会唤醒之前的一个处于阻塞状态的WorkQueue,如果依然没有待执行的任务,则会继续等待并退出,直到最后所有线程退出,即ForkJoinPool中没有核心线程数的概念,如果长期没有待执行的任务,线程池中所有线程都会退出。
final void runWorker(WorkQueue w) {
//初始化WorkQueue的任务数组
w.growArray(); // allocate queue
//hint初始值就是累加后的indexSeed
int seed = w.hint; // initially holds randomization hint
int r = (seed == 0) ? 1 : seed; // avoid 0 for xorShift
for (ForkJoinTask<?> t;;) {
if ((t = scan(w, r)) != null)
//将t和w中所有的未处理任务都执行完,t有可能是w中的,也可能是其他WorkQueue中的
w.runTask(t);
//如果scan返回null,说明所有的WorkQueue中都没有待执行的任务了,则调用awaitWork阻塞当前线程等待任务
//此时w已经被置为非激活状态
//awaitWork返回false,则终止for循环,线程退出,返回true则继续遍历
else if (!awaitWork(w, r))
break;
//计算下一个随机数
r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
}
}
//scan方法从r对应的一个WorkQueue开始遍历,查找待处理的任务,如果对应的WorkQueue为空或者没有待处理的任务,则遍历下一个WorkQueue
//直到所有的WorkQueue都遍历了两遍还没有找到待处理的任务,则返回null
//遍历第一遍没有找到时,oldSum不等于checkSum,被赋值成checkSum,遍历第二遍时,如果此期间没有新增的任务,则checkSum不变,oldSum等于checkSum,ss和scanState都变成负数,遍历第三遍时如果发现了一个待处理任务,则将oldSum和checkSum都置为0,如果还发现了一个待处理任务,则将最近休眠的WorkQueue,可能就是当前WorkQueue置为激活状态,并更新ss。如果第三遍遍历时还没有找到待处理的任务切期间没有新增的任务,则终止for循环,返回null,进入awaitWork方法。
private ForkJoinTask<?> scan(WorkQueue w, int r) {
WorkQueue[] ws; int m;
if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) { //workQueues已初始化
int ss = w.scanState; //scanState的初始值就是该元素在数组中的索引,大于0
for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
WorkQueue q; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
int b, n; long c;
if ((q = ws[k]) != null) { //如果k对应的数组元素不为null
if ((n = (b = q.base) - q.top) < 0 &&
(a = q.array) != null) { //如果有待处理的任务
long i = (((a.length - 1) & b) << ASHIFT) + ABASE;