在古代,皇帝要想办成一件事情,肯定不会亲自动手,而是任务细分给下面的大臣。下面的大臣也懒,于是把任务继续分给几个部分,继续下发,于是到了最后负责的人手里就完成一个小功能。上面的领导再把这些结果一层一层汇总。最终返回给皇帝,这就是分而制之的思想,也是我们今天的主题ForkJoin。
一、简介
从JDK1.7开始,Java就提供ForkJoin框架用于并执行任务,它的思想就是一个大任务分割成小任务,最终汇总每个小任务的结果的到大任务的结果。
1、ForkJoinPool
既然任务是被逐步细化的,那就需要把这些任务存在一个池子里面。这个池子就是ForkJoinPool,它与其他的ExecutorService区别主要在于区别主要在于它使用“工作窃取”,那么什么是“工作窃取呢”?
一个大任务被划分成无数个小任务,这些任务被分配到不同的队列,这些队列有些干活干的快,有些干活慢,于是干的快的,一看自己没任务需要执行了,就去隔壁的队列里面拿去任务执行。
2、ForkJoinTask
ForkJoinTask就是ForkJoinPool里面的每一个任务,他主要有两个子类:
RecursiveAction和RecursiveTask,然后通过fork()方法去分配任务执行任务,通过Join方法汇总任务结果。
这就整个过程的运用,他有两个子类,使用这两个子类都可以实现我们的任务分配和计算。
(1) RecursiveAction 一个递归无结果的ForkJoinTask(没有返回值)
(2) RecursiveTask 一个递归有结果的ForkJoinTask(有返回值)
ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成,ForkJoinTask数组负责存放程序提交给ForkJoinPool的任务,而ForkJoinWorkThread数组负责执行这行这些任务。
下面我们看看如何使用ForkJoin框架。
二、使用
1、RecursiveTask:有返回结果
(1)第一步:创建类MyRecursiveTask 继承RecursiveAction:
public class MyRecursiveTask extends RecursiveTask<Integer> {
private final static int threshold = 3;
private final int start;
private final int end;
public MyRecursiveTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
//如果任务分到不能再细分,那就直接计算
if (end - start <= threshold) {
return IntStream.rangeClosed(start, end).sum();
} else {
int mid = (start + end) / 2;
MyRecursiveTask leftTask = new MyRecursiveTask(start, mid);
MyRecursiveTask rightTask = new MyRecursiveTask(mid, end);
leftTask.fork();
rightTask.fork();
return leftTask.join() + rightTask.join();
}
}
}
(2)第二步:创建ForkJoinTest测试类:
public class ForkJoinTest {
public static void main(String[] args) {
try {
final ForkJoinPool pool =new ForkJoinPool();
ForkJoinTask<Integer> result= pool.submit(new MyRecursiveTask(0,100));
System.out.println("result = [" + result.get() + "]");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
2、RecursiveAction:无返回结果
(1)第一步:创建MyRecursiveAction继承RecursiveAction;
public class MyRecursiveAction extends RecursiveAction {
private final static int threshold = 3;
public static final AtomicInteger sum = new AtomicInteger(0);
private final int start;
private final int end;
public MyRecursiveAction(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected void compute() {
//如果任务拆分到不能再细分,那就计算
if (end - start <= threshold) {
sum.addAndGet(IntStream.rangeClosed(start, end).sum());
}else {
int mid = (start+end)/2;
MyRecursiveAction leftTask = new MyRecursiveAction(start,mid);
MyRecursiveAction rightTask = new MyRecursiveAction(mid,end);
leftTask.fork();
rightTask.fork();
}
}
}
(2)第二步:创建ForkJoinTest测试类:
public class ForkJoinTest {
public static void main(String[] args) {
try {
final ForkJoinPool pool = new ForkJoinPool();
pool.submit(new MyRecursiveAction(0, 100));
pool.awaitTermination(3, TimeUnit.SECONDS);
System.out.println("sum=" + MyRecursiveAction.sum);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}