CountedCompleter是ForkJoinTask的的子类,对ForkJoinTask做了一下扩展
CountedCompleter有以下特点:
- CountedCompleter维护两个动作:完成动作(onCompletion)和异常动作(onExceptionalCompletion),开发者可以重写这两个方法以实现自定义的动作内容;
- CountedCompleter维护一个父任务变量(completer),当前任务的完成情况、异常可通过这个变量逐级传递上去;
- CountedCompleter维护挂起任务(子任务)的计数(pending),挂起任务完成会触发父任务的pending减1,父任务pending计数减到0触发父任务完成动作(onCompletion);
- CountedCompleter依旧是个抽象类,核心的方法是compute,开发者需要自定这个方法,注意compute方法返回之前需要调用tryComplete;
- 作为一个ForkJoinTask,compute返回之后ForkJoinPool并没有将ForkJoinTask的状态置为Normal,如果此时调用join会阻塞线程,开发者可以在onCompletion调用this.quietlyComplete()设置Normal。
因为CountedCompleter这些特点,CountedCompleter方便将大任务逐级拆成子任务(subtask),直到最小单元(leaf task即叶子任务),叶子任务完成后逐级传递状态及结果。
CountedCompleter有一点比较不同,CountedCompleter执行完compute之后就运行结束,并没有等待子任务(如果有fork任务)结束。子任务完成后子任务调用tryComplete逐级判断任务是否完成。也就是说一个根任务fork出来的所有任务(无论是否叶子任务)都是并行的,ForkJoinPool调度执行是无区别对待的。所以,CountedCompleter调用compute并不会设置任务为Normal,因为如果设置Normal,调用join()就会立即返回,但此时子任务可能都还没有执行。源码如下(exec返回false,ForkJoinPool就不会设置其状态为Normal):
public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
......
//返回false,ForkJoinPool就不会设置任务完成
protected final boolean exec() {
compute();
return false;
}
......
}
CountedCompleter使用方法:就是在compute里面如果符合fork条件就fork子任务,如果不符合就执行具体的任务逻辑,最后调用tryComplete。tryComplete判断子任务是否都已经完成(pending==0)。如果是则执行onCompletion并传递到父任务执行相同逻辑;如果不是则将pending-1。
public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
......
public final void tryComplete() {
CountedCompleter<?> a = this, s = a;
for (int c;;) {
if ((c = a.pending) == 0) {
a.onCompletion(s);
//笔者注:如果当前任务完成(pending==0),执行完当前任务的完成动作(onCompletion),传递到父任务
if ((a = (s = a).completer) == null) {
s.quietlyComplete();
return;
}
}
//笔者注:当前任务没有完成(pending!=0),将pending-1
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
return;
}
}
......
}
一般来讲最后一个任务不调用fork而是直接调用该任务的compute方法,让当前线程继续执行最后一个子任务。因为线程在执行完最后一个任务的fork之后除了调用tryComplete没有执行任何实际逻辑。为了少一次线程调度,开发者可以直接让当前线程执行最后一个子任务(调用compute而不是fork),此时pending要少设置1。例如,根任务R的pending设置为2,运行根任务的线程直接调用叶子任务L2的compute;分支任务F1的pending设置为1,运行分支任务F1的线程直接调用叶子任务L4的compute。具体的使用方法CountedCompleter的作者Doug在类文件中有说明。