与 java8 文档中 CountedCompleter 的示例代码片段混淆
Confused with CountedCompleter's sample code fragment in java8 doc
我阅读了 java8 关于 CountedCompleter 的文档,其中给出了示例使用代码如下:
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;
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();
}
}
在compute
方法中,每个ForEach对象都会分叉两个子任务,并将pending count设置为2,
但是在compute
方法的最后,tryComplete
只能在pending count中减1,怎么办
剩下的呢??
看了ForkJoinPool
和CountedCompleter
的源码,终于明白了
从根 CountedCompleter 派生的所有 CountedCompleter 将像树一样组织。调用tryComplete
时,如果当前pending count为正,则减1。否则会调用当前CountedCompleter的onCompletion
,然后递归调用parentCountedCompleter上的tryComplete
。如果parent CountedCompleter是null
,说明它已经是root CountedCompleter,那么整个任务就完成了。
所以我们知道:
CountedCompleter
任务不会在compute()
方法完成后结束,它会等到pending count减到0
CountedCompleter
任务并不总是由根任务本身结束(与 RecursiveTask
和 RecursiveAction
非常不同),它可以由孩子的 tryComplete
然后我们看java8 doc中的代码片段(注意序号0:1:2:3:在代码中,是一种可能的执行顺序):
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;
setPendingCount(2); // 0: +2
new ForEach(this, array, op, mid, hi).fork(); // 2: -1
new ForEach(this, array, op, lo, mid).fork(); // 3: pending count == 0 complete
}
else if (hi > lo)
op.apply(array[lo]);
tryComplete(); // 1: -1
}
}
我阅读了 java8 关于 CountedCompleter 的文档,其中给出了示例使用代码如下:
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;
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();
}
}
在compute
方法中,每个ForEach对象都会分叉两个子任务,并将pending count设置为2,
但是在compute
方法的最后,tryComplete
只能在pending count中减1,怎么办
剩下的呢??
看了ForkJoinPool
和CountedCompleter
的源码,终于明白了
从根 CountedCompleter 派生的所有 CountedCompleter 将像树一样组织。调用tryComplete
时,如果当前pending count为正,则减1。否则会调用当前CountedCompleter的onCompletion
,然后递归调用parentCountedCompleter上的tryComplete
。如果parent CountedCompleter是null
,说明它已经是root CountedCompleter,那么整个任务就完成了。
所以我们知道:
CountedCompleter
任务不会在compute()
方法完成后结束,它会等到pending count减到0CountedCompleter
任务并不总是由根任务本身结束(与RecursiveTask
和RecursiveAction
非常不同),它可以由孩子的tryComplete
然后我们看java8 doc中的代码片段(注意序号0:1:2:3:在代码中,是一种可能的执行顺序):
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;
setPendingCount(2); // 0: +2
new ForEach(this, array, op, mid, hi).fork(); // 2: -1
new ForEach(this, array, op, lo, mid).fork(); // 3: pending count == 0 complete
}
else if (hi > lo)
op.apply(array[lo]);
tryComplete(); // 1: -1
}
}