我根据我对java中ForkJoinPool执行器服务的理解编写了一个小程序。程序的输出不符合要求。
我的理解: - 我对ForkJoinPool的理解是,它是另一种带有工作窃取功能的执行程序服务,它使一个线程从另一个队列中窃取工作,如果它完成了所有工作或已经空闲。这增加了线程对cpu的使用。 分叉用于将较大的任务分成较小的任务,直到任务足够简单以便顺序执行为止。 如果需要,连接将加入两个分开的任务以执行操作。
我的例子: - 基于上述理解,我尝试创建一个程序。任务是调用Parson对象的run方法。数组中有四个人对象。我希望四个线程在所有这些Person对象上并行工作,这意味着在一个包含四个对象的数组中,我的基本条件是数组,0,0。
预期输出: - Incompute(不确定多少次) 你好(4次) 线程ID(4次) 实际输出 - 在计算中多次打印并且没有任何时间打招呼。输出不是固定的,而是变化的。
有人可以指导我做出的错误吗?
package com.gc;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
public class FJMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
Person p4 = new Person();
Person[] array = new Person[4];
array[0] = p1;
array[1] = p2;
array[2] = p3;
array[3] = p4;
FJMain.MyRecursiveAction action = new FJMain().new MyRecursiveAction(array,0,3);
ForkJoinPool pool = new ForkJoinPool(4);
pool.invoke(action);
}
private class MyRecursiveAction extends RecursiveAction{
private Person[] array;
private int start;
private int end;
public MyRecursiveAction(Person[] array,int start,int end){
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected void compute() {
// TODO Auto-generated method stub
System.out.println("In compute");
int mid = (this.start + this.end)/ 2;
if(mid < 1){
computeDirectly(array,mid);
}else{
MyRecursiveAction subTask1 = new MyRecursiveAction(array, start, mid);
subTask1.fork();
MyRecursiveAction subTask2 = new MyRecursiveAction(array, mid + 1, end);
subTask2.fork();
}
}
private void computeDirectly(Person [] array,int end){
for(Person p:array){
p.name();
}
}
}
}
package com.gc;
public class Person {
public void name(){
System.out.println("Hello");
System.out.println(Thread.currentThread().getId());
}
}
修改
int beg = this.start;
int last = this.end;
int mid = (this.start + this.end)/ 2;
if((last - beg) < 1){
computeDirectly(array,beg);
}else{
MyRecursiveAction subTask1 = new MyRecursiveAction(array, start, mid);
subTask1.fork();
MyRecursiveAction subTask2 = new MyRecursiveAction(array, mid + 1, end);
subTask2.fork();
}
答案 0 :(得分:0)
您的RecursiveAction
会创建不会终止的递归任务。
new MyRecursiveAction(array, 0, 3);
创建
mid = (0 + 3) / 2 = 1
new MyRecursiveAction(array, 0, 1);
new MyRecursiveAction(array, 2, 3);
创建
mid = (0 + 1) / 2 = 0
completes
mid = (2 + 3) / 2 = 2
new MyRecursiveAction(array, 2, 2);
new MyRecursiveAction(array, 3, 3);
创建
mid = (2 + 2) / 2 = 2
new MyRecursiveAction(array, 2, 2);
new MyRecursiveAction(array, 3, 2);
mid = (3 + 3) / 2 = 3
new MyRecursiveAction(array, 3, 3);
new MyRecursiveAction(array, 4, 3);
令人作呕。其中一些将computeDirectly
,但大多数不会因为它们退化。
你必须修复你的基础案例和你复发的方式。
答案 1 :(得分:0)
您的错误是打印“inCompute”。拿出来,程序运行正常。 System.out是共享的,你永远不知道打印的确切顺序。
F / J程序如何操作任务是该程序的内部。看看来源;这足以吓唬任何人。每次调用计算都会向system.out发送一个字符串,无论是拆分还是执行computeDirectly()。
<强> EDIT1:强>
此输出为Hello / n 12 / n四次。 12是线程ID,它是相同的。这告诉我,分裂线程正在从它自己的双端队列中获取新任务,并在其他线程有机会参与之前处理它们。由于你只使用四个数组,这是有道理的。
尝试使用400万的数组。 F / J并不意味着在这么小的输入上运行。并行化这么小的输入是没有意义的。
如果您没有得到相同的结果,那么您的环境还有另一个问题。
答案 2 :(得分:0)
您需要更好地学习如何调试程序。为什么不完成?你为什么不看到你想要的四个“你好”?你应该先去哪儿看看?
嗯,computeDirectly
被击中了吗?不是,为什么?首先找出computeDirectly
未被击中的原因。 mid
的最小值是多少?如果您实际打印出值或调试了最小值mid
,您可能会注意到1
。
如果您将computeDirectly的条件更改为
if (mid <= 1) {
computeDirectly(array, mid);
}
您会看到预期的结果。
始终为1
,因为0 + 3 / 2
为1且无法进行任何分解。
答案 3 :(得分:0)
感谢所有回复。看起来在我的问题中编辑的基本条件看起来很好。我试图更改else条件并使用
中的代码http://rashidnoorani.blogspot.in/2012/08/how-to-use-fork-join-framework-features.html
我仍然试图找出根本原因,可能需要一些时间。我会回复这篇文章。这是更改后的代码,我可以看到Hello被打印4次。
@Override
protected void compute() {
// TODO Auto-generated method stub
int beg = this.start;
int last = this.end;
int mid = (this.start + this.end)/ 2;
System.out.println(this.start +" " +mid + " "+this.end);
if((last - beg) < 1){
computeDirectly(array,beg);
}else{
/*MyRecursiveAction subTask1 = new MyRecursiveAction(array, start, mid);
subTask1.fork();
MyRecursiveAction subTask2 = new MyRecursiveAction(array, mid + 1, end);
subTask2.fork();*/
invokeAll(new MyRecursiveAction(array, start, mid), new MyRecursiveAction(array, mid + 1, end));
}