一、简介
Fork/Join框架是
Java 7
提供的一个用于并行
执
行任
务
的框架,是一个把大任
务
分割成若干
个小任
务
,最
终汇总
每个小任
务结
果后得到大任
务结
果的框架。
我们
再通
过
Fork
和
Join
这
两个
单词
来理解一下
Fork/Join
框架。
Fork
就是把一个大任
务
切分
为
若干子任
务
并行的
执
行,
Join
就是合并
这
些子任
务
的
执
行
结
果,最后得到
这
个大任
务
的
结
果。比如
计
算
1+2+…+10000
,可以分割成
10
个子任
务
,每个子任
务
分
别对
1000
个数
进
行求和,
最
终汇总这
10
个子任
务
的
结
果。
二、流程
1.分割任务:首先我们需要有一个fork类来把大任务分割成子任务,有可能子任务还是很大,所以还需要不停地分割,直到分割出的子任务足够小。
2.执行任务并合并结果。分割的子任务分别放在双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程从队列里拿数据,然后合并这些数据。
三、工作窃取算法
工作窃取(
work-stealing
)算法是指某个
线
程从其他
队
列里窃取任
务
来
执行。
假如我
们
需要做一个比
较
大的任
务
,可以把
这
个任
务
分割
为
若干互不依赖
的子任
务
,
为
了减少
线
程
间
的
竞
争,把
这
些子任
务
分
别
放到不同的
队
列里,并
为
每个队列
创
建一个
单
独的
线
程来
执
行
队
列里的任
务
,
线
程和
队
列一一
对应
。比如
A
线
程
负责处
理
A 队列里的任
务
。但是,有的
线
程会先把自己
队
列里的任
务
干完,而其他
线
程
对应
的
队
列里
还
有 任务
等待
处
理。干完活的
线
程与其等着,不如去帮其他
线
程干活,于是它就去其他
线
程的
队
列里窃取一个任务
来
执
行。
而在
这时
它
们
会
访问
同一个
队
列,所以
为
了减少窃取任
务线
程和被窃取任务线
程之
间
的
竞
争,通常会使用双端
队
列,被窃取任
务线
程永
远
从双端
队
列的
头
部拿任务执
行,而窃取任
务
的
线
程永
远
从双端
队
列的尾部拿任
务执
行
优点:充分利用线程进行并行计算,减少了线程间的竞争。
缺点:在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且该算法会消耗了更多的系统资源,比如创建多个线程和多个双端队列。
四、ForkJoin应用
Fork/Join使用两个类来完成分治的操作
1.ForkJoinTask
我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务中执行fork()和join()操作的机制。通常情况下,我们不需要直接继承ForkJoinTask类,只需要继承它的子类,Fork/Join框架提供了以下两个子类。
·RecursiveAction
:用于没有返回
结
果的任
务
。
·RecursiveTask
:用于有返回
结
果的任
务
核心方法:
fork():在当前线程运行的线程池中创建一个子任务;join():模块子任务完成的时候返回任务结果;invoke():执行任务,也可以实时等待最终执行结果
2.ForkJoinPool
线程池最大的特点就是分叉(fork)合并(join)模式,将一个大任务拆分成多个小任务,并行执行,再结合工作窃取算法提高整体的执行效率,充分利用CPU资源。
3.实例
private static final Integer MAX = 200;
static class MyForkJoinTask extends RecursiveTask<Integer> {
// 子任务开始计算的值
private Integer startValue;
// 子任务结束计算的值
private Integer endValue;
public MyForkJoinTask(Integer startValue , Integer endValue) {
this.startValue = startValue;
this.endValue = endValue;
}
@Override
protected Integer compute() {
// 如果条件成立,说明这个任务所需要计算的数值分为足够小了
// 可以正式进行累加计算了
if(endValue - startValue < MAX) {
System.out.println("开始计算的部分:startValue = " + startValue + ";endValue = " + endValue);
Integer totalValue = 0;
for(int index = this.startValue ; index <= this.endValue ; index++) {
totalValue += index;
}
return totalValue;
}
// 否则再进行任务拆分,拆分成两个任务
else {
MyForkJoinTask subTask1 = new MyForkJoinTask(startValue, (startValue + endValue) / 2);
subTask1.fork();
MyForkJoinTask subTask2 = new MyForkJoinTask((startValue + endValue) / 2 + 1 , endValue);
subTask2.fork();
return subTask1.join() + subTask2.join();
}
}
}
public static void main(String[] args) {
// 这是Fork/Join框架的线程池
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> taskFuture = pool.submit(new MyForkJoinTask(1,1001));
try {
Integer result = taskFuture.get();
System.out.println("result = " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace(System.out);
}
}