ForkJoin 将大任务分割成小任务,若子任务还不够小则继续分割,最后执行任务合并结果。
- ForkJoinTask: 我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。他提供了在任务中执行fork()和join()操作机制。RecursiveTask有返回值的子类RecursiveAction 没有返回值的之类。
- ForkJoinPool :ForkJoinTask需要使用ForkJoinPool 来执行。
举例:下面计算1+2+3+…+9+10
每个子任务计算三个数相加
package com.john;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
public class ForkJoinTest {
//其中RecursiveTask<Integer>时ForkJoin实现类,直接继承即可。
//若子任务是没有返回值的则直接继承RecursiveAction 实现类。
public class CountTask extends RecursiveTask<Integer>{
private int s;
private int e;
private final static int THRESHOLD=3;
public CountTask(int s,int e)
{
this.s=s;
this.e=e;
}
@Override
protected Integer compute() {
int ret=0;
//每个子任务计算三个数相加
if(e-s>THRESHOLD)
{
int mid=(s+e)/2;
CountTask left=new CountTask(s, mid);
CountTask right=new CountTask(mid+1, e);
left.fork();
right.fork();
//取得子任务的结果并合并结果
int l=left.join();
int r=right.join();
ret=l+r;
}
else{
for(int i=s;i<=e;i++)
{
ret+=i;
}
}
System.out.println("当前线程名:"+Thread.currentThread().getName()+" 计算从"+s+"到"+e+"的结果为:"+ret);
return ret;
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ForkJoinPool pool=new ForkJoinPool();
ForkJoinTest test=new ForkJoinTest();
CountTask c=test.new CountTask(1, 10);
Future<Integer> future=pool.submit(c);
System.out.println(future.get().intValue());
}
}
输出结果:
当前线程名:ForkJoinPool-1-worker-1 计算从1到3的结果为:6
当前线程名:ForkJoinPool-1-worker-0 计算从9到10的结果为:19
当前线程名:ForkJoinPool-1-worker-2 计算从6到8的结果为:21
当前线程名:ForkJoinPool-1-worker-2 计算从6到10的结果为:40
当前线程名:ForkJoinPool-1-worker-3 计算从4到5的结果为:9
当前线程名:ForkJoinPool-1-worker-1 计算从1到5的结果为:15
当前线程名:ForkJoinPool-1-worker-1 计算从1到10的结果为:55
55
Fork/Join如何处理异常?
task.isCompletedAbnormally()用于判断任务是否有异常,若有,可以使用task.getException()获得异常信息。
实现原理:
ForkJoinTask 的fork()原理:
public final ForkJoinTask<V> fork() {
Thread t;
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
//push()将ForkJoinTest 压入队列,然后调用ForkJoinPool的singalWork()来唤醒或创建一个工作线程
((ForkJoinWorkerThread)t).workQueue.push(this);
else
ForkJoinPool.common.externalPush(this);
return this;
}
ForkJoinTask的join()方法原理
public final V join() {
int s;
if ((s = doJoin() & DONE_MASK) != NORMAL)
reportException(s);
return getRawResult();
}
//首先查看任务状态,若任务执行完成,直接返回结果,若为执行完,则从任务数组中取出并执行,若跑出异常,则记录异常,并将任务状态设置为EXCEPTIONAL
private int doJoin() {
int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
return (s = status) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(w = (wt = (ForkJoinWorkerThread)t).workQueue).
tryUnpush(this) && (s = doExec()) < 0 ? s :
wt.pool.awaitJoin(w, this) :
externalAwaitDone();
}