1、使用示例
import java.util.concurrent.CountedCompleter;
import java.util.concurrent.atomic.AtomicInteger;
public class CountedCompleterDemo {
interface Applier<E>{
void apply(E e);
}
static class ForEach<E> extends CountedCompleter<Void> {
public static <E> void forEach(E[] array, Applier<E> op) {
new ForEach<>(null, array, op, 0, array.length).invoke();
}
final E[] array;
final Applier<E> op;
final int lo, hi;
//p是父任务
ForEach(CountedCompleter<?> p, E[] array, Applier<E> op, int lo, int hi) {
super(p);
this.array = array;
this.op = op;
this.lo = lo;
this.hi = hi;
}
public void compute() {
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
setPendingCount(2); // must set pending count before fork
new ForEach(this, array, op, mid, hi).fork(); // right child
new ForEach(this, array, op, lo, mid).fork(); // left child
}
else if (hi > lo)
op.apply(array[lo]);
tryComplete();
}
}
public static void main(String[] args) throws Exception{
AtomicInteger sum=new AtomicInteger();
ForEach.forEach(new Integer[]{1,2,3,4,5,6},(element)->{
sum.addAndGet(element) ;
});
System.out.println("相加之和:"+sum.get());
}
}
2、countedCompleter是怎么开始执行的
ForkJoinPool会调用ForkJoinTask的doExec来执行任务,doExec方法如下:
final int doExec() {
int s; boolean completed;
if ((s = status) >= 0) {
try {
completed = exec();
} catch (Throwable rex) {
return setExceptionalCompletion(rex);
}
if (completed)
s = setCompletion(NORMAL);
}
return s;
}
这里通过调用exec方法来执行任务。exec是抽象方法,CountedCompleter中对exec的实现如下:
protected final boolean exec() {
compute();
return false;
}
这样compute的逻辑就是任务的核心计算逻辑,会最终被pool调用。
3、countedCompleter运行过程
在示例中,我们把最开始new出来的任务称为根任务,根任务在执行过程中,又fork出2个子任务,子任务可能会fork出自己的子任务,一层层fork下去后,最终一定有一个任务是不会再fork的,这个任务称为叶子任务。整个任务体系就是一颗完全二叉树。
整个执行过程,最核心的属性是:
pending:表示等待执行的任务
completer:表示任务的上级,即父任务
核心方法是tryComplete:
public final void tryComplete() {
CountedCompleter<?> a = this, s = a;
for (int c;;) {
if ((c = a.pending) == 0) {
a.onCompletion(s);
if ((a = (s = a).completer) == null) {
s.quietlyComplete();
return;
}
}
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
return;
}
}
整个执行过程如下图所示:
看一下invoke方法:
private int doInvoke() {
int s; Thread t; ForkJoinWorkerThread wt;
return (s = doExec()) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(wt = (ForkJoinWorkerThread)t).pool.
awaitJoin(wt.workQueue, this, 0L) :
externalAwaitDone();
}
这里根任务的doExec会被执行,即根任务被执行,之后就会join等待。所以根任务会等待,而子任务不会等待。而无论是pool的awaitJoin还是task的externalAwaitDone,都是通过helpComplete来帮助子任务执行
另外,这里所说的子任务不会等待,仅仅是以本例作为例子的特例情况,如果子任务中使用了join等方法,子任务也是会等待的。所以把根任务的概念推广一下,凡是需要等待的都称为根任务,不执行fork的称为叶子任务,不需等待,但是会fork的称为树干任务
4、helpComplete
核心实现思想:worker先尝试从自身队列中获取task或者task的子任务,若能获取到则执行,否则从其他队列窃取task或者task的子任务,若能窃取到则执行。不断如此,直到task完成。
源码如下:
/**
*
* @param w 执行子任务的worker
* @param task 等待的任务
* @param maxTasks表示helpComplete最多执行多少个子任务,为0表示一直执行,直到根任务完成
* @return task执行状态
*/
final int helpComplete(WorkQueue w, CountedCompleter<?> task,int maxTasks) {
WorkQueue[] ws; int s = 0, m;
if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 &&
task != null && w != null) {
int mode = w.config; // for popCC
int r = w.hint ^ w.top; // arbitrary seed for origin
int origin = r & m; // first queue to scan
int h = 1; // 1:ran, >1:contended, <0:hash
for (int k = origin, oldSum = 0, checkSum = 0;;) {
CountedCompleter<?> p; WorkQueue q;
if ((s = task.status) < 0)
break;
//尝试从当前worker的工作队列中获取task或者其子任务来执行
if (h == 1 && (p = w.popCC(task, mode)) != null) {
p.doExec(); // run local task
if (maxTasks != 0 && --maxTasks == 0)
break;
origin = k; // reset
oldSum = checkSum = 0;
}
else { // poll other queues
if ((q = ws[k]) == null)
h = 0;
//小于0表示q为空,或者q有非CountedCompleter的任务
else if ((h = q.pollAndExecCC(task)) < 0)
checkSum += h;
if (h > 0) {
//如果h==1,说明上面q.pollAndExecCC(task)执行成功了一个task或者task的子任务
if (h == 1 && maxTasks != 0 && --maxTasks == 0)
break;
r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
origin = k = r & m; // move and restart
oldSum = checkSum = 0;
}
//如果(k = (k + 1) & m) == origin成立,说明整个workQueues数组被遍历了一遍
else if ((k = (k + 1) & m) == origin) {
//要满足oldSum == (oldSum = checkSum),h=q.pollAndExecCC(task)的结果必须≥0
//而这个if是在else if内,所以h=0,即要满足这个条件,需要workQueues数组的所有
//队列都为null
if (oldSum == (oldSum = checkSum))
break;
checkSum = 0;
}
}
}
}
return s;
}
5、popCC
/**
* 获取task的子任务
* @param task 父任务
* @param mode worker的mode
* @return task或task的子任务
*/
final CountedCompleter<?> popCC(CountedCompleter<?> task, int mode) {
int s; ForkJoinTask<?>[] a; Object o;
if (base - (s = top) < 0 && (a = array) != null) {
long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
if ((o = U.getObjectVolatile(a, j)) != null &&
(o instanceof CountedCompleter)) {
CountedCompleter<?> t = (CountedCompleter<?>)o;
for (CountedCompleter<?> r = t;;) {
if (r == task) {
//如果是公共队列,可能有多个worker更改它的top,所以需要加锁。非公共队列仅能
//该队列的worker操作它的top
if (mode < 0) { // must lock
if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
if (top == s && array == a &&
U.compareAndSwapObject(a, j, t, null)) {
U.putOrderedInt(this, QTOP, s - 1);
U.putOrderedInt(this, QLOCK, 0);
return t;
}
U.compareAndSwapInt(this, QLOCK, 1, 0);
}
}
else if (U.compareAndSwapObject(a, j, t, null)) {
U.putOrderedInt(this, QTOP, s - 1);
return t;
}
break;
}
else if ((r = r.completer) == null) // try parent
break;
}
}
}
return null;
}
6、pollAndExecCC
/**
* 从base位置开始获取任务,如果是task的子任务或者就是task,则执行之
* @param task 父任务
* @return 子任务执行状态。1表示执行成功,2表示窃取到的任务为null,可以重试,-1表示非空,但跟task不匹配
* 若队列为空,则为负数,且剩下31位的值为该队列的base
*/
final int pollAndExecCC(CountedCompleter<?> task) {
int b, h; ForkJoinTask<?>[] a; Object o;
if ((b = base) - top >= 0 || (a = array) == null)
h = b | Integer.MIN_VALUE; // to sense movement on re-poll
else {
long j = (((a.length - 1) & b) << ASHIFT) + ABASE;
if ((o = U.getObjectVolatile(a, j)) == null)
h = 2; // retryable
else if (!(o instanceof CountedCompleter))
h = -1; // unmatchable
else {
CountedCompleter<?> t = (CountedCompleter<?>)o;
for (CountedCompleter<?> r = t;;) {
if (r == task) {
if (base == b &&
U.compareAndSwapObject(a, j, t, null)) {
base = b + 1;
t.doExec();
h = 1; // success
}
else
h = 2; // lost CAS
break;
}
else if ((r = r.completer) == null) {
h = -1; // unmatched
break;
}
}
}
}
return h;
}