Fork/Join框架
定义
Fork/Join是Java7提供的一个用于并行执行任务的框架,也是一种通过并行方式获得良好的并行计算性能的一种简单有效的设计技术,通过递归,把任务分为可以执行的子任务,然后并行执行这些子任务,等所有任务都结束的时候,再合并最终结果。
核心
Fork/Join的核心在于轻量级调度机制,采用了Work-Stealing的调度策略。
即:
每一个工作线程维护自己的调度队列中的可运行任务,队列以双端队列的形式被维护。
对于一个给定的工作线程来说,任务所产生的子任务将被放入工作者自己的双端队列中。
工作线程使用后进先出(LIFO)的顺序,通过弹出任务来处理队列中的任务。
当一个工作线程的本地没有任务执行的时候,它将使用先进先出(FIFO)的规则尝试随机从别的工作线程中偷窃任务去执行。
框架介绍
Fork/Join使用两个类来完成任务分割和任务结果合并:
- ForkJoinTask
使用ForkJoin框架,必须首先创建ForkJoin任务。它提供在任务重执行fork()和join()操作的机制。通常情况下我们不需要直接继承ForkJoinTask类,而只要继承它的子类,Fork/Join框架提供了以下两个子类:
RecursiveAction - 用于没有返回结果的任务
RecursiveTask - 用于有返回结果的任务 - ForkJoinPool
ForkJoinTask需要通过ForkJoinPool来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务是,它会随机从其它线程队列的尾部获取一个任务。
代码示例
package fj;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
public class CountTask extends RecursiveTask {
private static final int THRESHOLD = 2;//阈值
private int start;
private int end;
public CountTask(int start,int end) {
this.start= start;
this.end= end;
}
@Override
protected Integer compute() {
int sum = 0;
//如果任务足够小就计算任务
boolean canCompute = (end-start) <=THRESHOLD;
if(canCompute) {
for(int i =start; i <=end; i++) {
sum += i;
}
}else{
//如果任务大于阀值,就分裂成两个子任务计算
int middle = (start+end) / 2;
CountTask leftTask =new CountTask(start, middle);
CountTask rightTask =new CountTask(middle + 1,end);
//执行子任务
leftTask.fork();
rightTask.fork();
//等待子任务执行完,并得到其结果
intleftResult = leftTask.join();
intrightResult = rightTask.join();
//合并子任务
sum = leftResult + rightResult;
}
return sum;
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool =new ForkJoinPool();
//生成一个计算任务,负责计算1+2+3+4
CountTask task =new CountTask(1, 4);
//执行一个任务
Future result = forkJoinPool.submit(task);
try{
System.out.println(result.get());
}catch(InterruptedException e) {
}catch(ExecutionException e) {
}
}
}
异常处理
ForkJoinTask在执行的时候可能会抛出异常,可以通过isComplatedAbnormally()方法来检查任务是否已经抛出异常或者已经被取消了
if(task.isCompletedAbnormally())
{
System.out.println(task.getException());
}