ForkJoinPool#awaitQuiescence实际上是如何工作的?

我有下一个实现RecursiveAction,这个类的单一目的 - 是从 0 到 9 打印,但如果可能的话,从不同的线程打印:

public 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();
        }
    }
}

我认为调用awaitQuiescence将使当前线程等待所有任务(提交和分叉)完成:

public class Main {
    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");
    }
}

但我并不总是得到正确的结果,而不是打印 10 次,打印 0 到 10 次。

但是,如果我添加helpQuiesce到我的实现中RecursiveAction

public 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();
        }

        RecursiveAction.helpQuiesce();//here
    }
}

一切正常。

我想知道究竟在awaitQuiescence等待什么?

回答

当你改变你得到的会发生什么的想法System.out.println(num);System.out.println(num + " " + Thread.currentThread());

这可能会打印如下内容:

0 Thread[ForkJoinPool-1-worker-3,5,main]
1 Thread[main,5,main]
tasks
2 Thread[ForkJoinPool.commonPool-worker-3,5,main]

awaitQuiescence检测到有待处理的任务时,它会通过窃取一个并直接执行来提供帮助。它的文档说:

如果由在此池中运行的 ForkJoinTask 调用,则等效于 ForkJoinTask.helpQuiesce()。否则,等待和/或尝试协助执行任务,直到此池 isQuiescent() 或指示的超时过去。

重点是我加的

这发生在这里,我们可以看到,一个任务打印“main”作为它的执行线程。然后, 的行为fork()被指定为:

安排在当前任务正在运行的池中异步执行此任务(如果适用),或使用ForkJoinPool.commonPool()if not inForkJoinPool()

由于该main线程不是 a 的工作线程ForkJoinPool,所以fork()会将新任务提交给commonPool()。从那时起,fork()从公共池的工作线程调用的 将提交下一个任务到公共池。但是awaitQuiescence在自定义池上调用不会等待公共池的任务完成并且 JVM 过早终止。

如果你要说这是一个有缺陷的 API 设计,我不会反对。

解决方案不是awaitQuiescence用于公共池¹。通常,RecursiveAction拆分子任务的 a 应该等待它们完成。然后,您可以等待根任务完成以等待所有关联任务完成。

此答案的后半部分包含此类RecursiveAction实现的示例。

¹awaitQuiescence当您没有实际的期货时很有用,例如提交到公共池的并行流。


以上是ForkJoinPool#awaitQuiescence实际上是如何工作的?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>