本文目录:
1 Fork/Join概述
Fork/Join是JDK中提供的类似Map/Reduce的并行计算的实现;它主要处理那些可以递归的分解成更小作业的作业。
与Map/Reduce类似,它也分成两个过程:
- Fork:大的作业被分成很多小的作业分别提交到线程池中执行;
- Join:小作业完成后通过Join操作合并每个作业的执行结果;
示例 假设有10000个数据要计算其合计值,那么我们可以将这10000个数分成100个作业,每个作业执行100个数据的合计值;这100个作业被提交到线程池中并行运行;这就是Fork的过程。
每个小作业执行会有一个结果,Join就是负责将这100个作业的执行结果合并起来。
Fork/Join采用工作窃取算法业充分利用每个线程;每个线程可能被分配多个任务;当分配给某个线程的任务全部执行完成时,它将会窃取原本分配给其它线程的任务并执行,这样能尽量提高计算效率。
说明 关于工作窃取,按上面所述的10000个数的计算操作,也可以这样理解:A线程被分配了100个数据的计算任务,这100个数的任务又可能被细分成更小粒度的10个数的计算任务;这些子任务都是分配给A来执行的;假设分配给另外的B线程的任务已经执行完成,而A中还剩下有子任务未执行完成,那B会从A的队列中取得未执行的子任务来执行。
Fork/Join计算框架的核心是ForkJoinPool这个类,它继承自AbstractExecutorService类,实现了工作窃取算法。而ForkJoinTask的任务可以被提交到ForkJoinPool中执行。
2 示例
先以一简单的例子来说明Fork/Join如何使用。
以第一节中所述的例子来演示,计算10000个数的和。
public static void main(String[] args) {
//生成10000个随机数
List<Integer> list = Stream.generate(() -> (int)(Math.random() * 10)).limit(10000)
.collect(Collectors.toList());
//创建ForkJoinTask对象
ForkJoinTask<Integer> forkJoinTask = new MyForkJoinTask(list);
//提交到ForkJoinPool中
ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
Integer result = forkJoinPool.invoke(forkJoinTask);
//比较计算结果与Stream的计算结果是否一致
System.out.println(result + ", " + list.stream().reduce((x, y) -> x + y).get());
}
/**
* 并行计算传入的List中所有元素的和
*/
public static class MyForkJoinTask extends ForkJoinTask<Integer>{
private int result =