我阅读了有关CountedCompleter的java8文档,其中提供了示例用法代码,如下所示:

 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对象将派生两个子任务并将未决计数设置为2,
但是在compute方法的末尾,tryComplete只能将待处理计数减1,
剩下的一个吗?

最佳答案

阅读ForkJoinPoolCountedCompleter的源代码后,我终于明白了。

从根CountedCompleter派生的所有CountedCompleter都将像树一样组织。当您调用tryComplete时,如果当前暂挂计数为正,它将减1。否则,它将调用当前CountedCompleter的onCompletion,然后递归调用父CountedCompleter的tryComplete。如果父级CountedCompleter是null,则意味着它已经是根CountedCompleter,则整个任务完成。

因此,我们知道:


CountedCompleter任务在完成compute()方法后不会结束,它将等待,直到待处理的计数减为0
CountedCompleter任务并不总是由根任务本身结束(与RecursiveTaskRecursiveAction完全不同),它可以由孩子的tryComplete结束


然后让我们看一下Java 8 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
   }
 }

10-06 02:19