Java 8 ForkJoinPool“等待静止”实际上是如何工作的?

Java 8 ForkJoinPool“等待静止”实际上是如何工作的?,java-8,forkjoinpool,Java 8,Forkjoinpool,我有一个递归动作的下一个实现,这个类的唯一目的是从0打印到9,但如果可能,从不同的线程打印: public class MyRecursiveAction extends RecursiveAction { private final int num; public MyRecursiveAction(int num) { this.num = num; } @Override protected void compute() {

我有一个递归动作的下一个实现,这个类的唯一目的是从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();
        }
    }
}
但我并不总是得到正确的结果,不是打印10次,而是从0到10次打印

但是如果我将
帮助静止
添加到我的
递归操作的实现中

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
    }
}
公共类MyRecursiveAction扩展了RecursiveAction{
私有最终整数;
公共MyRecursiveAction(int num){
this.num=num;
}
@凌驾
受保护的void compute(){
如果(数值<10){
系统输出打印项数(num);
新建MyRecursiveAction(num+1).fork();
}
RecursiveAction.helpQuiesce();//此处
}
}
一切正常


我想知道什么是真正的等待静止等待?

您知道当您更改
系统.out.println(num)时会发生什么
to
System.out.println(num+“”+Thread.currentThread())

这可能会打印以下内容:

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();
        }
    }
} 
0线程[ForkJoinPool-1-worker-3,5,main]
1线程[主,5,主]
任务
2线程[ForkJoinPool.commonPool-worker-3,5,main]
waitquiesion
检测到有挂起的任务时,它会通过窃取一个任务并直接执行来帮助解决问题。说:

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

我补充的重点

正如我们所看到的,这发生在这里,任务打印“main”作为其执行线程。然后,的行为被指定为:

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

由于
main
线程不是
ForkJoinPool
的工作线程,因此
fork()
将把新任务提交给
commonPool()
。从那时起,从公共池的工作线程调用的
fork()
也将向公共池提交下一个任务。但是,在自定义池上调用的
waitquiesion
不会等待公共池任务的完成,JVM会过早终止

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

解决方案是不将
等待静止
用于公共池以外的任何对象。通常,分割子任务的
RecursiveAction
应该等待其完成。然后,您可以等待根任务的完成,以等待所有相关任务的完成

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

如果您没有实际的未来,比如提交到公共池的并行流,则“等待静止”
非常有用

一切正常

不,它没有,你很幸运,当你插入:

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));
}
这会使
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.awaittquiesion(…)
,其文档记录如下:

否则,等待和/或尝试协助执行任务

它并不是说它一定会等待,而是说它会“等待和/或尝试…”,在这种情况下,它是更多或更多、更多和更多。因此,它将尝试提供帮助,但仍然不会等待所有任务完成。这是奇怪还是愚蠢

插入
RecursiveAction.helpQuiesce()时
您最终调用的是相同的
等待静止
(使用不同的参数)引擎盖下-因此本质上没有任何变化;根本问题仍然存在:

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

    }

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

    }

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