ForkJoin是用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。
// Instance fields
volatile long ctl; // 控制中心:非常重要,看下图解析
volatile int runState; // 负数是shutdown,其余都是2的次方
final int config; // 配置:二进制的低16位代表 并行度(parallelism),
//高16位:mode可选FIFO_QUEUE(1 << 16)和LIFO_QUEUE(1 << 31),默认是LIFO_QUEUE
int indexSeed; // 生成worker的queue索引
volatile WorkQueue[] workQueues; // main registry
final ForkJoinWorkerThreadFactory factory;
final UncaughtExceptionHandler ueh; // per-worker UEH
final String workerNamePrefix; // to create worker name string
volatile AtomicLong stealCounter; // also used as sync monitor
// Instance fields
volatile int scanState; // 负数:inactive, 非负数:active, 其中奇数代表scanning
int stackPred; // sp = (int)ctl, 前一个队列栈的标示信息,包含版本号、是否激活、以及队列索引
int nsteals; // 窃取的任务数
int hint; // 一个随机数,用来帮助任务窃取,在 helpXXXX()的方法中会用到
int config; // 配置:二进制的低16位代表 在 queue[] 中的索引,
// 高16位:mode可选FIFO_QUEUE(1 << 16)和LIFO_QUEUE(1 << 31),默认是LIFO_QUEUE
volatile int qlock; // 锁定标示位:1: locked, < 0: terminate; else 0
volatile int base; // index of next slot for poll
int top; // index of next slot for push
ForkJoinTask>[] array; // 任务列表
final void externalPush(ForkJoinTask> task) {
WorkQueue[] ws; WorkQueue q; int m;
//我们以前常用的Random,在并发下,多个线程同时计算种子需要用到同一个原子变量。
//由于更新操作使用CAS,同时执行只有一个线程成功,其他线程的大量自旋造成性能损失,ThreadLocalRandom继承Random,对此进行了改进。
//ThreadLocalRandom运用了ThreadLocal,每个线程内部维护一个种子变量,多线程下计算新种子时使用线程自己的种子变量进行更新,避免了竞争。
int r = ThreadLocalRandom.getProbe();
int rs = runState;
// 外部提交的task,肯定会到偶数位下标的队列上
// SQMASK = 0x007e = 1111110,任何数和 SQMASK 进行 & 运算 都会是偶数
if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
(q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&
//队列上锁
U.compareAndSwapInt(q, QLOCK, 0, 1)) {
ForkJoinTask>[] a; int am, n, s;
if ((a = q.array) != null &&
(am = a.length - 1) > (n = (s = q.top) - q.base)) {
int j = ((am & s) << ASHIFT) + ABASE;
//把 task 放到队列的 top端
U.putOrderedObject(a, j, task);
U.putOrderedInt(q, QTOP, s + 1);
U.putIntVolatile(q, QLOCK, 0);
if (n <= 1)
signalWork(ws, q);
return;
}
//队列解锁
U.compareAndSwapInt(q, QLOCK, 1, 0);
}
externalSubmit(task);
}
final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
//......
if ((ws = workQueues) != null && (n = ws.length) > 0) {
int s = indexSeed += SEED_INCREMENT; // unlikely to collide
int m = n - 1;
// worker的queue肯定放在pool中的queue[]中的奇数下标
// m = ws.lenght - 1, ws.lenght 肯定是偶数,则m 肯定是奇数
// 1的二进制位:00000001, 所以任何数 "|" 1 都是奇数
// 所以 奇数 & 奇数 , 1&1 = 1,所以i肯定是奇数
i = ((s << 1) | 1) & m; // odd-numbered indices
if (ws[i] != null) { // collision
int probes = 0; // step by approx half n
int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
// 如果下标已经有队列,则重新生成奇数下标
// step肯定为偶数:EVENMASK:0xfffe:1111111111111110
// 所以 奇数+偶数,奇偶性不变
while (ws[i = (i + step) & m] != null) {
if (++probes >= n) {
workQueues = ws = Arrays.copyOf(ws, n <<= 1);
m = n - 1;
probes = 0;
}
}
}
//...
}
//......
}
private ForkJoinTask> scan(WorkQueue w, int r) {
WorkQueue[] ws; int m;
if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) {
int ss = w.scanState; // initially non-negative
// k = r & m 。 r是一个随机数,m 是 队列数组长度 - 1;用于定位去哪个 队列 窃取 task
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) {
// 如果有还没执行的task,尝试窃取队列q 中的base下标的 task。 即FIFO
// i: 在内存中,b下标对应的对象的偏移值。 a.length - 1 的二进制位 永远是 0[1...]s,所以 (a.length - 1) & b = b,主要是保证了b不会越界
if ((n = (b = q.base) - q.top) < 0 &&
(a = q.array) != null) { // non-empty
long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
if ((t = ((ForkJoinTask>)
U.getObjectVolatile(a, i))) != null &&
q.base == b) {
// ss 是小偷的 scanState,大于0代表当前的worker是激活的
if (ss >= 0) {
// 把 task 从 队列中取出来,然后队列的base+1,如果被窃取的队列中有多于1个的task,则尝试唤醒其他的worker
if (U.compareAndSwapObject(a, i, t, null)) {
q.base = b + 1;
if (n < -1) // signal others
signalWork(ws, q);
return t;
}
}
// ss小于0代表当前的worker是未激活的,并且当前是第一次扫描,这时候尝试激活worker
// oldSum: 上一次遍历周期的 base 值的和。
// (int) c : 可以拿到当前栈顶的空闲worker。sp = (int) c
else if (oldSum == 0 && // try to activate
w.scanState < 0)
tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
}
if (ss < 0) // refresh
ss = w.scanState;
// 更新随机值,重新初始化所有控制变量,重新定位队列
r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
origin = k = r & m; // move and rescan
oldSum = checkSum = 0;
continue;
}
checkSum += b;
}
// 每次没有窃取到task的时候,都会k+1(k值不会超过m),当k遍历了一圈还没有steal到任务,则当前小偷worker是过剩的,则inactive这个小偷worker
if ((k = (k + 1) & m) == origin) { // continue until stable
// oldSum == (oldSum = checkSum) 实际上就是 oldSum == checkSum , oldSum = checkSum
// oldSum == checkSum 是判断 这个周期和上个周期 的base和是否一直,如果一直, 说明base可能没有变过
if ((ss >= 0 || (ss == (ss = w.scanState))) &&
oldSum == (oldSum = checkSum)) {
if (ss < 0 || w.qlock < 0) // already inactive
break;
int ns = ss | INACTIVE; // try to inactivate
long nc = ((SP_MASK & ns) |
(UC_MASK & ((c = ctl) - AC_UNIT)));
// 维护 队列的 stack,可以指向前一个栈顶的队列
w.stackPred = (int)c; // hold prev stack top
U.putInt(w, QSCANSTATE, ns);
if (U.compareAndSwapLong(this, CTL, c, nc))
ss = ns;
else
w.scanState = ss; // back out
}
checkSum = 0;
}
}
}
return null;
}
final void signalWork(WorkQueue[] ws, WorkQueue q) {
long c; int sp, i; WorkQueue v; Thread p;
// AC是负数,所以 active worker不足
while ((c = ctl) < 0L) { // too few active
// sp:第一位是0,没有版本号,没有inactive的worker
if ((sp = (int)c) == 0) { // no idle workers
//tc: tc不为0,就是代表 total worker - parallelism < 0, 所以需要添加worker
if ((c & ADD_WORKER) != 0L) // too few workers
tryAddWorker(c);
break;
}
if (ws == null) // unstarted/terminated
break;
// 取栈顶的worker,如果下标已经越界或queue为null,线程池都是终止了
if (ws.length <= (i = sp & SMASK)) // terminated
break;
if ((v = ws[i]) == null) // terminating
break;
// 新的scanState,版本+1,设置状态为激活,INACTIVE = 1 << 31,~INACTIVE = 01111111....
int vs = (sp + SS_SEQ) & ~INACTIVE; // next scanState
// 确认 worker的 sp没有变化
int d = sp - v.scanState; // screen CAS
// 生成新的 ctl,(UC_MASK & (c + AC_UNIT))设置 高32位, (SP_MASK & v.stackPred)设置低32位
long nc = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & v.stackPred);
if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) {
//激活worker
v.scanState = vs; // activate v
if ((p = v.parker) != null)
U.unpark(p);
break;
}
//当前queue没有task 需要执行了,则停止signal
if (q != null && q.base == q.top) // no more work
break;
}
}