Путать с фрагментом кода CountedCompleter в документе java8 - PullRequest
0 голосов
/ 06 марта 2020

Я прочитал java8 do c about 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 будет форкать две подзадачи и установить значение в ожидании 2, но в конце метода compute, tryComplete может уменьшить только один в счетчике в ожидании, как остальные в ?? ?? 1009 *

1 Ответ

0 голосов
/ 08 марта 2020

После прочтения исходного кода ForkJoinPool и CountedCompleter я, наконец, понял его.

Все CountedCompleter, полученные из root CountedCompleter, будут организованы как дерево. Когда вы вызываете tryComplete, если текущий ожидающий счет положителен, он уменьшается на 1. В противном случае он вызовет onCompletion текущего CountedCompleter, а затем рекурсивно вызовет tryComplete для родительского CountedCompleter. Если родительский CountedCompleter равен null, это означает, что он уже root CountedCompleter, тогда вся задача завершена.

Итак, мы знаем, что:

  • CountedCompleter задача не будет завершается после того, как метод compute() завершен, он будет ожидать, пока ожидаемое число уменьшится до 0
  • CountedCompleter задача не всегда завершается самой задачей root (сильно отличается от RecursiveTask и RecursiveAction ), его можно завершить с помощью childs 'tryComplete

Затем давайте посмотрим фрагмент кода в java 8 do c (обратите внимание на порядковый номер 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
   }
 }
...