Все работает нормально.
Нет, вам повезло, что это сработало, когда вы вставили:
RecursiveAction.helpQuiesce();
Чтобы объяснить это, давайте немного изменим ваш пример:
static class MyRecursiveAction extends RecursiveAction {
private final int num;
public MyRecursiveAction(int num) {
this.num = num;
}
@Override
protected void compute() {
if (num < 10) {
System.out.println(num);
new MyRecursiveAction(num + 1).fork();
}
}
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.execute(new MyRecursiveAction(0));
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
}
Если вы запустите это, вы заметите, что получаете результат, который ожидаете получить. И на это есть две основные причины. Во-первых, метод fork
выполнит задачу в common pool
, как уже объяснялось в другом ответе. Во-вторых, потоки в общем пуле являются потоками daemon. JVM не ждет их завершения перед выходом, она существует раньше. Так что, если это так, вы можете спросить, почему это работает. Это происходит из-за этой строки:
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
что заставляет поток main
(который не является потоком демона) спать в течение двух секунд, давая достаточно времени для ForkJoinPool
для выполнения вашей задачи.
Теперь давайте изменим код ближе к вашему примеру:
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.execute(new MyRecursiveAction(0));
System.out.println(forkJoinPool.awaitQuiescence(5, TimeUnit.SECONDS) ? "tasks" : "time");
}
в частности, вы используете: forkJoinPool.awaitQuiescence(...)
, который задокументирован как:
В противном случае ждет и/или пытается помочь в выполнении задач...
Он не говорит, что будет обязательно ждать, он говорит, что будет ждать и/или попытку..., в данном случае это больше или, чем и. Таким образом, он попытается помочь, но все равно не будет ждать завершения всех задач. Это странно или даже глупо?
Когда вы вставляете RecursiveAction.helpQuiesce();
, вы в конечном итоге вызываете один и тот же awaitQuiescence
(с другими аргументами) под капотом - так что по существу ничего не меняется; основная проблема осталась:
static ForkJoinPool forkJoinPool = new ForkJoinPool();
static AtomicInteger res = new AtomicInteger(0);
public static void main(String[] args) {
forkJoinPool.execute(new MyRecursiveAction(0));
System.out.println(forkJoinPool.awaitQuiescence(5, TimeUnit.SECONDS) ? "tasks" : "time");
System.out.println(res.get());
}
static class MyRecursiveAction extends RecursiveAction {
private final int num;
public MyRecursiveAction(int num) {
this.num = num;
}
@Override
protected void compute() {
if (num < 10_000) {
res.incrementAndGet();
System.out.println(num + " thread : " + Thread.currentThread().getName());
new MyRecursiveAction(num + 1).fork();
}
RecursiveAction.helpQuiesce();
}
}
Когда я запускаю это, оно никогда не печатает 10000
, показывая, что вставки этой строки ничего не меняют.
Обычный способ обработки таких вещей по умолчанию — fork
, а затем join
. И еще один join
в звонилке, на ForkJoinTask
, который вы получаете при вызове submit
. Что-то вроде:
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool(2);
ForkJoinTask<Void> task = forkJoinPool.submit(new MyRecursiveAction(0));
task.join();
}
static class MyRecursiveAction extends RecursiveAction {
private final int num;
public MyRecursiveAction(int num) {
this.num = num;
}
@Override
protected void compute() {
if (num < 10) {
System.out.println(num);
MyRecursiveAction ac = new MyRecursiveAction(num + 1);
ac.fork();
ac.join();
}
}
}
person
Eugene
schedule
10.06.2021