记录下get到的新东西——java的Fork/Join框架。
1、什么是Fork/Join框架?
Fork/Join框架是java 7提供的一个用于并行执行任务的框架,主要用于将一个比较大的任务分割成若干个小任务,最终再把分割后的任务结果汇总起来,最后得到最终结果的一个框架。
其中:Fork就是把一个大任务切分为若干子任务的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。
比如:筛选10000张符合某种条件的图片,可以切分成100个子任务,每个子任务分别对100张图片进行筛选,最后汇总这100个子任务的结果得到最终结果。
因此,使用Fork/Join框架主要分为两个步骤:1、分割任务;2、执行任务并合并结果。
2、Fork/Join框架使用两个类来完成上面两件事。
1)ForkJoinTask:我们要使用ForkJoin框架,则就需要先创建一个ForkJoin任务。他提供在任务中执行fork()和join()操作的机制。通常情况下,我们不需要直接继承ForkJoinTask类,而是直接继承子类:
- RecursiveAction:不需要返回结果的任务
- RecursiveTask:有返回值的任务
2)ForkJoinPool:ForkJoinTask需要通过ForkJoinPool才可以执行。
【任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务】
下面用数字连加做个demo
1、创建一个类,继承RecursiveTask。
/**
* 累计加法任务类,例如:从1加到100。
*/
class CountTask extends RecursiveTask<Integer> {
// 一个任务最小的加法个数:4
private static final int THRESHOLD = 4;
private int start;// 加数的起始值
private int end;// 加数的结束值.例如:从4加到14,start就是4,end就是14
public CountTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
int sum = 0;
// 判断任务是否可以被向下拆分(加的个数是否超过了阈值)
boolean canCompute = (start - end) > THRESHOLD;
if (canCompute) {// 超过了阈值拆分成两个子任务
int middle = (start + end) / 2;//获取中间值
// 从中间值为分割点,分派给两个子任务(子任务还会自己判断是否应该再拆分,依次递归)
CountTask leftTask = new CountTask(start, middle);
CountTask rightTask = new CountTask(middle + 1, end);
// fork两个任务
leftTask.fork();
rightTask.fork();
// 获取任务执行完毕后的结果
int left = leftTask.join();
int right = rightTask.join();
// 将子任务的结果相加
sum = left + right;
} else {// 如果没有超过阈值,直接连加即可
for (int i = start; i <= end; i++) {
sum += i;
}
}
return sum;
}
2、通过ForkJoinPool执行任务
public class ForkJoin {
public static void main(String[] args) {
// 创建任务,从1到100连加
CountTask task = new CountTask(1,100);
// 创建ForkJoinPool 用于执行任务
ForkJoinPool pool = new ForkJoinPool();
// 执行任务
Future<Integer> result = pool.submit(task);
try {
// 打印执行的结果(5050)
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
说在后面的话:
异常处理,如果ForkJoinTask在执行的时候可能会抛出异常,但是,我们没办法在主线程里直接捕获异常,所以,ForkJoinTask提供了isCompletedAbnormally()方法来检查任务是否已经抛出异常或已经被取消了,并且可以通过ForkJoinTask的getException()方法获取异常。
ForkJoinPool pool = new ForkJoinPool();
CountTask task = new CountTask(1,100);
Future<Integer> result = pool.submit(task);
// 判断是否异常终止
if(task.isCompletedAbnormally()){
// 打印异常信息
System.out.println(task.getException());
}