CountedCompleter是ForkJoinTask的一个子类。
其可以简单理解为处理业务和数量有关的一些FJT,一般分为如下几类:
定义:
FJT = ForkJoinTask
FJP = ForkJoinPool
说明: 需要看如何使用的,直接查看下面的官方示例。
/**
* 这是一个FJT,会在任务完成时检查是否有等待的任务,若没有则触发一个完成动作。
* 总的来说,与其他形式的forkjointask相比,CountedCompleters在存在子任务停滞
* 和阻塞的情况下更健壮,但对编程的直觉较差。CountedCompleter的使用类似于其他基
* 于完成的组件(例如 java.nio.channels.CompletionHandler),除了需要多个挂
* 起的完成来触发完成操作 onCompletion(CountedCompleter),而不是一个。
* 除非另外初始化,否则 getPendingCount pending count从0开始,但是可以使用 link
* setPendingCount、addToPendingCount和compareAndSetPendingCount(原子地)更
* 改。在调用 tryComplete时,如果挂起的动作计数为非零,则递减;否则,将执行完成操作,
* 如果这个完成器本身有一个完成器,则继续使用它的完成器。与相关的同步组件
* (如 java.util.concurrent.Phaser 和 java.util.concurrent.Semaphore )相同。
* 这些方法只影响内部计数;他们没有建立任何进一步的内部簿记。特别是,未完成任务的标识没有
* 得到维护。如下所示,您可以创建子类来记录一些或所有挂起的任务或它们的结果。如下所示,
* 还提供了支持自定义完成遍历的实用程序方法。但是,因为CountedCompleters只提供了基
* 本的同步机制,所以创建进一步的抽象子类来维护链接、字段和其他适合于一组相关用法的支
* 持方法可能是有用的。
*
* 具体的CountedCompleter类必须定义方法{@link #compute},在大多数情况下(如下所
* 示),在返回之前应该调用{@code tryComplete()}一次。该类还可以选择性地覆盖方法
* {@link #onCompletion(CountedCompleter)}来在正常完成时执行一个动作,以及方
* 法{@link #onExceptionalCompletion(Throwable, CountedCompleter)}来在任
* 何异常时执行一个动作。
*
* CountedCompleters通常不产生结果,在这种情况下,它们通常被声明为
* {@code CountedCompleter},并且总是返回{@code null}作为结果值。在其他情况下,
* 您应该覆盖方法{@link #getRawResult}来提供来自{@code join()、invoke()}和相
* 关方法的结果。通常,该方法应该返回CountedCompleter对象的一个字段(或一个或多个字
* 段的函数)的值,该对象在完成时保存结果。默认情况下,方法{@link #setRawResult}在
* CountedCompleters中不起作用。重写此方法以维护包含结果数据的其他对象或字段是可能
* 的,但很少适用。
*
* 一个CountedCompleter本身没有一个completer(即,其中{@link #getCompleter}返
* 回{@code null})可以作为一个常规的ForkJoinTask与这个增加的功能。
* 但是,任何有另一个completer的completer只是作为其他计算的内部助手,所以它自己的任
* 务状态(如{@link * ForkJoinTask#isDone}等方法中报告的)是任意的;这种状态只有
* 在显式调用{@link #complete}、{@link ForkJoinTask#cancel}、
* {@link ForkJoinTask# complete(Throwable)}或方法{@code compute}异常完成时
* 才会改变。在任何异常完成之后,如果存在一个任务的完成器,并且它还没有完成,则可以将异
* 常传递给任务的完成器(以及它的完成器,等等)。类似地,取消内部的CountedCompleter只
* 会对该完井器产生局部影响,所以通常不会有用。
*
* 用例
* bookkeeping=簿记;
* 并行递归分解。CountedCompleters可能被安排在与{@link RecursiveAction}类似的树
* 中,尽管设置它们所涉及的结构通常是不同的。这里,每个任务的完成者是计算树中的父任务。
* 即使它们需要更多的簿记,当将可能耗时的操作(不能进一步细分)应用到数组或集合的每个元素
* 时,CountedCompleters可能是更好的选择;特别是当某些元素完成操作所需的时间与其他元
* 素明显不同时,这可能是由于内部变化(例如I/O),也可能是由于诸如垃圾收集之类的辅助效果。
* 因为CountedCompleters提供了它们自己的延续,其他线程不需要阻塞来执行它们。
*
* 例如,下面是一个类的初始版本,它使用两个递归分解来将工作划分为单个部分(叶子任务)。即使
* 将工作划分为单独的调用,基于树的技术通常也比直接fork叶子任务更好,因为它们减少了线程间
* 的通信并改善了负载平衡。在递归情况下,每对要完成的叶子任务的第二个触发其父任务的完成(
* 因为没有执行结果组合,所以方法{@code onCompletion}的默认无操作实现没有被覆盖)。
* 静态实用程序方法设置基本任务并调用它(这里使用{@link ForkJoinPool#commonPool()
* }隐式地调用它)。
*
*
* 触发器。有些CountedCompleters本身并不是fork的,而是作为其他设计中的管道的一部分;
* 包括那些完成一个或多个异步任务触发另一个异步任务的部分。例如:
* For example:
*
* class HeaderBuilder extends CountedCompleter<...> { ... }
* class BodyBuilder extends CountedCompleter<...> { ... }
* class PacketSender extends CountedCompleter<...> {
* PacketSender(...) { super(null, 1); ... } // 在第二次完成时触发onCompletion
* public void compute() { } // never called
* public void onCompletion(CountedCompleter> caller) { sendPacket(); }
* }
* // sample use:
* PacketSender p = new PacketSender();
* new HeaderBuilder(p, ...).fork();
* new BodyBuilder(p, ...).fork();
* }
*
* @since 1.8
* @author Doug Lea
*/
public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
private static final long serialVersionUID = 5232453752276485070L;
/** This task's completer, or null if none */
//PS:一个链表结构,完成的触发顺序则是先进后出的模式
final CountedCompleter<?> completer;
/** 在完成前等待的任务数量 */
volatile int pending;
/**
* PS: 传入的completer一般是‘父’任务的实例引用
* @param completer this task's completer, or {@code null} if none
* @param 初始PendingCount
*/
protected CountedCompleter(CountedCompleter<?> completer,
int initialPendingCount) {
this.completer = completer;
this.pending = initialPendingCount;
}
protected CountedCompleter(CountedCompleter<?> completer) {
this.completer = completer;
}
protected CountedCompleter() {
this.completer = null;
}
/**
* 这个任务完成的主要计算。
*/
public abstract void compute();
/**
* 调用了无条件complete或者在调用tryComplete时,如果等待的任务数量是0,则触发此方法。
* 默认情况下,此方法不执行任何操作。您可以通过检查给定调用方参数的标识来区分情况。
* 如果不等于this,那么它通常是一个子任务,可能包含要组合的结果(和/或到其他结果的链接)。
*
* @param caller the task invoking this method (which may
* be this task itself)
*/
public void onCompletion(CountedCompleter<?> caller) {
}
/**
* 调用方法completeexception (Throwable)或方法compute抛出异常时执行操作,
* 且此任务尚未正常完成。进入这个方法时,这个任务FJTk#iscompletedwrong。
* 这个方法的返回值控制了进一步的传播:如果 true 并且这个任务有一个未完成的完成
* 器,那么这个完成器也会异常地完成,与这个完成器有相同的异常。这个方法的默认实
* 现只返回 true。
* @param ex the exception
* @param caller the task invoking this method (which may
* be this task itself)
* @return {@code true} if this exception should be propagated to this
* task's completer, if one exists
*/
public boolean onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller) {
return true;
}
/**
* 返回在此任务的构造函数中建立的完成器, or null if none.
*
* @return the completer
*/
public final CountedCompleter<?> getCompleter() {
return completer;
}
/**
* return当前等待的数量
*
* @return the current pending count
*/
public final int getPendingCount() {
return pending;
}
/**
* @param count the count
*/
public final void setPendingCount(int count) {
pending = count;
}
/**
* (原子地)将给定的值添加到挂起的计数
* @param delta the value to add
*/
public final void addToPendingCount(int delta) {
U.getAndAddInt(this, PENDING, delta);
}
/**
* 只有在当前持有给定的期望值时,才会(原子地)将挂起的计数设置为给定的计数。
*
* @param expected the expected value
* @param count the new value
* @return {@code true} if successful
*/
public final boolean compareAndSetPendingCount(int expected, int count) {
return U.compareAndSwapInt(this, PENDING, expected, count);
}
/**
* If the pending count is nonzero, (atomically) decrements it.
*
* @return the initial (undecremented) pending count holding on entry
* to this method
*/
public final int decrementPendingCountUnlessZero() {
int c;
do {} while ((c = pending) != 0 &&
!U.compareAndSwapInt(this, PENDING, c, c - 1));
return c;
}
/**
* Returns the root of the current computation; i.e., this
* task if it has no completer, else its completer's root.
* PS: 遍历单向链表,找到root节点
* @return the root of the current computation
*/
public final CountedCompleter<?> getRoot() {
CountedCompleter<?> a = this, p;
while ((p = a.completer) != null)
a = p;
return a;
}
/**
* 如果挂起的计数为非零,则减少计数;否则调用 onCompletion(CountedCompleter),
* 然后类似地尝试完成此任务的completer(如果存在的话),否则将此任务标记为完成。
* PS:a.onCompletion(s); 表示完成的当前的任务,s = a 这里指向父任务
* 如果父任务==null,则完成整个任务链的任务;否则重新进入循环执行逻辑
*/
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;
}
}
/**
* PS: 类似于tryComplete,但是在不需要调用每个‘子’任务的onCompletion方法时,
* 可以使用此方法
*/
public final void propagateCompletion() {
CountedCompleter<?> a = this, s = a;
for (int c;;) {
if ((c = a.pending) == 0) {
if ((a = (s = a).completer) == null) {
s.quietlyComplete();
return;
}
}
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
return;
}
}
/**
* 当获得多个子任务的任何一个(而不是所有)结果时,此方法可能非常有用。
* 但是,在不覆盖 setRawResult 的常见(也是推荐的)情况下,
* completeroot();可以更简单地获得这种效果。
* PS:适用于findAny这种场景
*
* @param rawResult the raw result
*/
public void complete(T rawResult) {
CountedCompleter<?> p;
setRawResult(rawResult);
onCompletion(this);
quietlyComplete();
if ((p = completer) != null)
p.tryComplete();
}
/**
* 如果此任务的挂起计数为零,则返回此任务;否则递减其挂起计数并返回 null。
* 此方法被设计为在完成遍历循环中与 nextComplete一起使用。
*
* @return this task, if pending count was zero, else {@code null}
*/
public final CountedCompleter<?> firstComplete() {
for (int c;;) {
if ((c = pending) == 0)
return this;
else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
return null;
}
}
/**
* 如果此任务没有完成器,则调用FJT#quiet complete并返回null
* 或者,如果完成器的挂起计数非零,则减除该挂起计数并返回 null
* 否则,返回完成器。
* 此方法可用于同构任务层次结构的完成遍历循环的一部分:
*
* {@code
* for (CountedCompleter> c = firstComplete();
* c != null;
* c = c.nextComplete()) {
* // ... process c ...
* }}
* PS: 简单来说,就是遍历查找完成的任务。
* @return the completer, or {@code null} if none
*/
public final CountedCompleter<?> nextComplete() {
CountedCompleter<?> p;
if ((p = completer) != null)
return p.firstComplete();
else {
quietlyComplete();
return null;
}
}
/**
* 等效于getRoot().quietlyComplete()
* PS: 遍历到root的completer,执行其quietlyComplete方法
*/
public final void quietlyCompleteRoot() {
for (CountedCompleter<?> a = this, p;;) {
if ((p = a.completer) == null) {
a.quietlyComplete();
return;
}
a = p;
}
}
/**
* 如果当前task尚未完成,则尝试去运行指定数量的未运行的完成路径上的任务
* (如果已知存在该任务)
*
* @param maxTasks the maximum number of tasks to process. If
* less than or equal to zero, then no tasks are
* processed.
*/
public final void helpComplete(int maxTasks) {
Thread t; ForkJoinWorkerThread wt;
if (maxTasks > 0 && status >= 0) {
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
(wt = (ForkJoinWorkerThread)t).pool.
helpComplete(wt.workQueue, this, maxTasks);
else
ForkJoinPool.common.externalHelpComplete(this, maxTasks);
}
}
/**
* 支持FJT的异常传播
*/
void internalPropagateException(Throwable ex) {
CountedCompleter<?> a = this, s = a;
while (a.onExceptionalCompletion(ex, s) &&
(a = (s = a).completer) != null && a.status >= 0 &&
a.recordExceptionalCompletion(ex) == EXCEPTIONAL)
;
}
/**
* 为CountedCompleters实现执行约定
*/
protected final boolean exec() {
compute();
return false;
}
/**
* 一般会覆盖重写实现
*
* @return the result of the computation
*/
public T getRawResult() { return null; }
/**
* 包含结果的CountedCompleters可以选择使用的方法来帮助维护结果数据。
* 默认情况下,什么也不做。不建议重写。但是,如果重写此方法以更新现有对
* 象或字段,则通常必须将其定义为线程安全的.
*/
protected void setRawResult(T t) { }
// Unsafe mechanics
private static final sun.misc.Unsafe U;
private static final long PENDING;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
PENDING = U.objectFieldOffset
(CountedCompleter.class.getDeclaredField("pending"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/**
* 可以注意到,在递归情况下,任务在for它的右任务之后什么也不做,因此可以在返回之前直接调用它的左
* 任务,从而改进这种设计。(这类似于尾部递归移除。)此外,由于任务在执行其左任务时返回(而不是通
* 过调用{@code tryComplete}),挂起计数被设置为1:
*/
class MyOperation<E> { void apply(E e) { ... } }
class ForEach<E> extends CountedCompleter<Void> {
public static <E> void forEach(E[] array, MyOperation<E> op) {
new ForEach<E>(null, array, op, 0, array.length).invoke();
}
final E[] array; final MyOperation<E> op; final int lo, hi;
ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
super(p);
this.array = array; this.op = op; this.lo = lo; this.hi = hi;
}
public void compute() { // version 1
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
//PS: 这里手动设置等待的任务,实质上就是等待左右两个部分的任务进行fork
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();
}
}
//改进版本:
class ForEach<E> ...
public void compute() { // version 2
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
setPendingCount(1); // only one pending
new ForEach(this, array, op, mid, hi).fork(); // right child
new ForEach(this, array, op, lo, mid).compute(); // direct invoke
}
else {
if (hi > lo)
op.apply(array[lo]);
tryComplete();
}
}
}</pre>
/* 作为进一步的改进,请注意左边的任务甚至不需要存在。
* 我们可以使用原始任务进行迭代,并为每个fork添加一个挂起计数,而不是创建一个新任务。
* 此外,由于此树中没有任何任务实现{@link #onCompletion(CountedCompleter)}方法,
* 所以{@code tryComplete()}可以替换为{@link #propagateCompletion}。
*/
//PS: 这里通过循环实质上分解了 原来‘左’边的任务,而‘右’边的任务,则通过new ForEach自身
//的compute自行处理(即当成全部任务递归分解)。
class ForEach<E> ...
public void compute() { // version 3
int l = lo, h = hi;
while (h - l >= 2) {
int mid = (l + h) >>> 1;
addToPendingCount(1);
new ForEach(this, array, op, mid, h).fork(); // right child
h = mid;
}
if (h > l)
op.apply(array[l]);
propagateCompletion();
}
}
//应用的一个搜索,在分段搜索中,找到一个,就结束。
class Searcher<E> extends CountedCompleter<E> {
final E[] array; final AtomicReference<E> result; final int lo, hi;
Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
super(p);
this.array = array; this.result = result; this.lo = lo; this.hi = hi;
}
public E getRawResult() { return result.get(); }
public void compute() { // similar to ForEach version 3
int l = lo, h = hi;
while (result.get() == null && h >= l) {
if (h - l >= 2) {
int mid = (l + h) >>> 1;
addToPendingCount(1);
new Searcher(this, array, result, mid, h).fork();
h = mid;
}
else {
E x = array[l];
if (matches(x) && result.compareAndSet(null, x))
quietlyCompleteRoot(); // root task is now joinable
break;
}
}
tryComplete(); // normally complete whether or not found
}
boolean matches(E e) { ... } // return true if found
public static <E> E search(E[] array) {
return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
}
}}
//PS: 这种简化的MapReduce适用于无顺序要求的分解与归纳操作
//将所有别的节点当成兄弟节点;都可以直接进行归纳操作。
class MyMapper<E> { E apply(E v) { ... } }
class MyReducer<E> { E apply(E x, E y) { ... } }
class MapReducer<E> extends CountedCompleter<E> {
final E[] array; final MyMapper<E> mapper;
final MyReducer<E> reducer; final int lo, hi;
MapReducer<E> sibling;
E result;
MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
MyReducer<E> reducer, int lo, int hi) {
super(p);
this.array = array; this.mapper = mapper;
this.reducer = reducer; this.lo = lo; this.hi = hi;
}
public void compute() {
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
left.sibling = right;
right.sibling = left;
setPendingCount(1); // only right is pending
right.fork();
left.compute(); // directly execute left
}
else {
if (hi > lo)
result = mapper.apply(array[lo]);
tryComplete();
}
}
public void onCompletion(CountedCompleter<?> caller) {
if (caller != this) {
MapReducer<E> child = (MapReducer<E>)caller;
MapReducer<E> sib = child.sibling;
if (sib == null || sib.result == null)
result = child.result;
else
result = reducer.apply(child.result, sib.result);
}
}
public E getRawResult() { return result; }
public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
return new MapReducer<E>(null, array, mapper, reducer,
0, array.length).invoke();
}
}}
//PS: 改进版,通过next来连接MapReducer,使之有序。
class MapReducer<E> extends CountedCompleter<E> { // version 2
final E[] array; final MyMapper<E> mapper;
final MyReducer<E> reducer; final int lo, hi;
MapReducer<E> forks, next; // record subtask forks in list
E result;
MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
super(p);
this.array = array; this.mapper = mapper;
this.reducer = reducer; this.lo = lo; this.hi = hi;
this.next = next;
}
public void compute() {
int l = lo, h = hi;
while (h - l >= 2) {
int mid = (l + h) >>> 1;
addToPendingCount(1);
(forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork();
h = mid;
}
if (h > l)
result = mapper.apply(array[l]);
// process completions by reducing along and advancing subtask links
for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next)
t.result = reducer.apply(t.result, s.result);
}
}
public E getRawResult() { return result; }
public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
return new MapReducer<E>(null, array, mapper, reducer,
0, array.length, null).invoke();
}
}}
ForkJoin框架之CountedCompleter