1.什么是ForkJoin?
ForkJoin是由JDK1.7后提供多线并发处理框架,ForkJoin的框架的基本思想是分而治之。使用ForkJoin将相同的计算任务通过多线程的进行执行。从而能提高数据的计算速度。通过多线程提高大数据的处理。处理大数据是使用ForkJoin的前提,数据较小时不建议使用!
2.主要思想
分而治之:ForkJoin将一个复杂的任务,按照设定的阈值进行分解成多个子任务,然后将各个子任务结果进行汇总。如图:当然实际就按阈值来拆分子任务
3.使用
- 使用ForkJoin框架,需要创建一个ForkJoin的任务。因为ForkJoin框架为我们提供了RecursiveAction和RecursiveTask。我们只需要继承ForkJoin为我们提供的抽象类的其中一个并且实现compute方法。
- RecursiveTask在进行exec之后会使用一个result的变量进行接受返回的结果。
- 而RecursiveAction在exec后是不会保存返回结果。
使用步骤:
1.FrokJoin需要用过forkJoinPool来执行
2.再计算任务forkJoinPool.execute或者forkJoinPool.sumbit
3.计算类需要继承RecursiveTask或RecursiveAction
代码示例:计算0~10_0000_0000相加的值
package com.kuang.forkjoin;
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
//临界值
private Long temp = 10000L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
//计算方法
@Override
protected Long compute() {
if((end-start)<temp){
Long sum = 0L;
for (long i = start; i<= end; i++) {
sum += i;
}
return sum;
}else{
//forkjoin
long middle = (start+end)/2;
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork();
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork();
return task1.join() + task2.join();
}
}
}
测试类:
package com.kuang.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
public class SumTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1();//5479
test2(); //4011
}
//普通方法
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (long i = 1; i <= 10_0000_0000; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum:"+sum+" "+(end-start));
}
//ForkJoin框架
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinDemo task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
final Long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println("sum:"+sum+" "+(end-start));
}
}
测试结果:
明显效率提高了!
4.特点:工作窃取
-
任务进行分解成多个子任务的时候,每个子任务的处理时间都不一样。
-
例如分别有子任务A和B。如果子任务B某一时刻已经执行完毕,子任务A还在执行。那么如果子任务B的线程等待子任务A完毕后在进行汇总,那么子任务B线程就会在浪费执行时间,最终的执行时间就以最耗时的子任务为准。
-
而如果子任务B执行完毕后,处理子任务A的任务,并且执行完毕后将任务归还给子任务A。这样就可以提高执行效率,这就是工作窃取。
不过有时子任务B去窃取子任务B的任务时,子任务A也刚好执行到那个任务,两者就会发生争夺,这是它的弊端