Java 8 ForkJoinPool“等待静止”实际上是如何工作的?
我有一个递归动作的下一个实现,这个类的唯一目的是从0打印到9,但如果可能,从不同的线程打印: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() {
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)时会发生什么
toSystem.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();
}
}
}