После прочтения исходного кода 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
}
}