并发编程从零开始(十六)-ForkJoinPool
第四部分:ForkJoinPool
15 ForkJoinPool用法
ForkJoinPool就是JDK7提供的一种“分治算法”的多线程并行计算框架。Fork意为分叉,Join意为合并,一分一合,相互配合,形成分治算法。此外,也可以将ForkJoinPool看作一个单机版的Map/Reduce,多个线程并行计算。
相比于ThreadPoolExecutor,ForkJoinPool可以更好地实现计算的负载均衡,提高资源利用率。
假设有5个任务,在ThreadPoolExecutor中有5个线程并行执行,其中一个任务的计算量很大,其余4个任务的计算量很小,这会导致1个线程很忙,其他4个线程则处于空闲状态。
利用ForkJoinPool,可以把大的任务拆分成很多小任务,然后这些小任务被所有的线程执行,从而实现任务计算的负载均衡。
例子1:快排
快排有2个步骤:
-
利用数组的第1个元素把数组划分成两半,左边数组里面的元素小于或等于该元素,右边数组里面的元素比该元素大;
-
对左右的两个子数组分别排序。
左右两个子数组相互独立可以并行计算。利用ForkJoinPool:
例子2:求1到n个数的和
上面的代码用到了 RecursiveAction 和 RecursiveTask 两个类,它们都继承自抽象类ForkJoinTask,用到了其中关键的接口 fork()、join()。二者的区别是一个有返回值,一个没有返回值。
RecursiveAction/RecursiveTask类继承关系:
在ForkJoinPool中,对应的接口如下:
16 核心数据结构
与ThreadPoolExector不同的是,除一个全局的任务队列之外,每个线程还有一个自己的局部队列。
核心数据结构如下所示:
下面看一下这些核心数据结构的构造过程:
17 工作窃取队列
关于上面的全局队列,有一个关键点需要说明:它并非使用BlockingQueue,而是基于一个普通的