目录
使用示例
ForkJoin使用场景主要是并行计算,将大任务不断拆分,拆分成足够小任务,多线程并行运算,最后汇总结果。
fork-join在任务计算耗时比较长的时候,进行拆分是比较划算的,如果不是耗时长的任务,拆分成本高,计算速度也不一定比不拆分快。还有就是任务拆分粒度也影响任务计算的速度,任务不是越拆得小越好,任务拆分过小消耗资源,也会降低计算速度。
下面是一个使用的简单示例,代码如下:
public class SumTask extends RecursiveTask<Long> {
private static final Integer THRESHOLD = 100000;
private long start;
private long end;
public SumTask(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long sum = 0;
boolean isOk = end - start <= THRESHOLD;
if (isOk) {
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}
//除以2
long middle = (end + start) / 2;
//子任务递归
SumTask sumSubTask = new SumTask(start, middle - 1);
SumTask sumSubTask1 = new SumTask(middle, end);
//fork子任务
invokeAll(sumSubTask, sumSubTask1);
//join子任务
long join = sumSubTask.join();
long join1 = sumSubTask1.join();
sum = join + join1;
//计算结果
return sum;
}
public static void normalCompute(long end) {
long beginAt = System.currentTimeMillis();
long sum = 0;
for (long i = 0; i <= end; i++) {
sum += i;
}
long endAt = System.currentTimeMillis();
System.out.println("计算结果2为 sum = " + sum + ",计算时长为" + "--- " + (endAt - beginAt) + "ms");
}
public static void main(String[] args) {
long end = 10000000000L;
normalCompute(end);
forkJoinCompute(end);
}
private static void forkJoinCompute(long end) {
long beginAt = System.currentTimeMillis();
long join = new ForkJoinPool().submit(new SumTask(0, end)).join();
long endAt = System.currentTimeMillis();
System.out.println("fork-Join计算结果为 sum = " + join + ",计算时长为" + "--- " + (endAt - beginAt) + "ms");
}
}
- 当end值时1000000的时候,三次测试结果如下:
1 | 普通计算(ms) | fork-join(ms) |
2 | 3 | 13 |
3 | 5 | 18 |
4 | 4 | 14 |
- 当end值时1000000000的时候,三次测试结果如下:
1 | 普通计算(ms) | fork-join(ms) |
2 | 782 | 265 |
3 | 768 | 278 |
4 | 809 | 278 |
- 当end值时1000000000的时候,fork-join任务拆粒度对比
threshold | 100000000 | 1000 |
2 | 224 | 287 |
3 | 280 | 291 |
4 | 208 | 311 |
ForkJoin原理
ForkJoinTask就是ForkJoinPool里面的每一个任务。他主要有两个子类:RecursiveAction和RecursiveTask。然后通过fork()方法去分配任务执行任务,通过join()方法汇总任务结果,
这就是整个过程的运用。他有两个子类,使用这两个子类都可以实现我们的任务分配和计算。
(1)RecursiveAction 一个递归无结果的ForkJoinTask(没有返回值)
(2)RecursiveTask 一个递归有结果的ForkJoinTask(有返回值)
数据结构
ForkJoinPool:中含有一个workQueues队列;
workQueues:由ForkJoinTask数组和workerThread和指向ForkJoinPool的引用;
ForkJoinTask数组负责存放程序提交给ForkJoinPool的任务,而workerThread数组负责执行这些任务,ForkJoinPool的引用是为了当ForkJoinTask数组中的任务处理完之后再次获取任务交给workerThread进行处理。整个结构大致如下图: