原文地址:《Fork/Join框架的简单使用》
1、什么是Fork/Join框架
fork操作的作用是把一个大的任务划分成若干个较小的任务。在这个划分过程一般是递归进行的。直到将任务划分到合适的大小为止。需要恰当地选取子问题的大小。太大的子问题不利于通过并行方式来提高性能,而太小的子问题则会带来较大的额外开销。每个子问题计算完成后,可以得到关于整个问题的部分解。join操作的作用是把这些分解任务的结果组织起来,得到完整解。 简单的说,ForkJoin其核心思想就是分治。Fork分解任务,Join收集数据。
2、Fork/Join框架简单使用实例
JDK1.7之后,jdk中添加了相关的Fork/Join代码,主要分为任务实现代码和任务执行代码,每个具体的任务都需要继承RecursiveTask 和RecursiveAction,其中RecursiveTask任务执行后会返回结果,而RecursiveAction不返回结果,这里给出RecursiveTask使用代码
import java.util.concurrent.RecursiveTask;
public class SumTask extends RecursiveTask{
private int start;
private int end;
private long[] datas;
public SumTask(int start,int end,long[] datas){
this.start = start;
this.end = end;
this.datas = datas;
}
@Override
protected Long compute() {
if(end-start<50000){
long sum = 0L;
for(int i = start;i<end;i++){
sum += datas[i];
}
return sum;
}
int middle = start+(end-start)/2;
SumTask leftTask = new SumTask(start,middle,datas);
SumTask rightTask = new SumTask(middle,end,datas);
leftTask.fork();
rightTask.fork();
return leftTask.join()+rightTask.join();
}
}
这是一个简单的累加任务,通过使用SumTask对300000000个long型数据进行累加,通过与正常的for循环累加速度对比,可以发现使用SumTask的累加速度要优于for循环的累加速度(Fork/Join框架相关变量初始化时间除外)
3、代码测试结果
使用main方法对SumTask进行累加速度测试,测试代码如下:
private static Random random = new Random();
public static void main(String[] args){
long[] datas = new long[300000000];
for(int i = 0;i<300000000;i++){
datas[i] = random.nextLong();
}
ForkJoinPool pool = new ForkJoinPool();
long start = 0;
SumTask task;
long result = 0;
for (int j = 0 ;j <10;j++) {
result = 0;
System.out.println("开始for循环累加计算");
start = System.currentTimeMillis();
for(int i = 0;i<300000000;i++){
result+=datas[i];
}
System.out.println("for循环累加计算耗时:"
+(System.currentTimeMillis()-start)+"\n累加结果:"+result+"\n");
result = 0;
System.out.println("开始累加计算");
start = System.currentTimeMillis();
task = new SumTask(0,datas.length,datas);
pool.execute(task);
result = task.join();
System.out.println("累加计算耗时:"
+(System.currentTimeMillis()-start)+"\n累加结果:"+result+"\n");
}
}
输出结果如下:
开始for循环累加计算
for循环累加计算耗时:166
累加结果:-405325633085518385
开始累加计算
累加计算耗时:117
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:219
累加结果:-405325633085518385
开始累加计算
累加计算耗时:92
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:157
累加结果:-405325633085518385
开始累加计算
累加计算耗时:88
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:160
累加结果:-405325633085518385
开始累加计算
累加计算耗时:94
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:160
累加结果:-405325633085518385
开始累加计算
累加计算耗时:94
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:172
累加结果:-405325633085518385
开始累加计算
累加计算耗时:93
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:158
累加结果:-405325633085518385
开始累加计算
累加计算耗时:97
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:161
累加结果:-405325633085518385
开始累加计算
累加计算耗时:93
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:162
累加结果:-405325633085518385
开始累加计算
累加计算耗时:92
累加结果:-405325633085518385
开始for循环累加计算
for循环累加计算耗时:163
累加结果:-405325633085518385
开始累加计算
累加计算耗时:95
累加结果:-405325633085518385
结果中可以看到,在首次运行速度上比对是,由于Fork/Join框架使用时需要相关线程池以及线程队列、任务队列的初始化,其运行的时间统计要高于后期的耗时,在后续由于相关的线程池以及线程队列、任务队列以及初始化完成,通过对比结果可以看出for循环累加慢于fork/Join任务累加速度