并发编程从零开始(十六)-ForkJoinPool

并发编程从零开始(十六)-ForkJoinPool

第四部分:ForkJoinPool

15 ForkJoinPool用法

ForkJoinPool就是JDK7提供的一种“分治算法”的多线程并行计算框架。Fork意为分叉,Join意为合并,一分一合,相互配合,形成分治算法。此外,也可以将ForkJoinPool看作一个单机版的Map/Reduce,多个线程并行计算。

相比于ThreadPoolExecutor,ForkJoinPool可以更好地实现计算的负载均衡,提高资源利用率。

假设有5个任务,在ThreadPoolExecutor中有5个线程并行执行,其中一个任务的计算量很大,其余4个任务的计算量很小,这会导致1个线程很忙,其他4个线程则处于空闲状态。

利用ForkJoinPool,可以把大的任务拆分成很多小任务,然后这些小任务被所有的线程执行,从而实现任务计算的负载均衡。

例子1:快排

快排有2个步骤:

  1. 利用数组的第1个元素把数组划分成两半,左边数组里面的元素小于或等于该元素,右边数组里面的元素比该元素大;

  2. 对左右的两个子数组分别排序。

左右两个子数组相互独立可以并行计算。利用ForkJoinPool:

并发编程从零开始(十六)-ForkJoinPool_第1张图片

并发编程从零开始(十六)-ForkJoinPool_第2张图片

例子2:求1到n个数的和

并发编程从零开始(十六)-ForkJoinPool_第3张图片

上面的代码用到了 RecursiveAction 和 RecursiveTask 两个类,它们都继承自抽象类ForkJoinTask,用到了其中关键的接口 fork()、join()。二者的区别是一个有返回值,一个没有返回值。

RecursiveAction/RecursiveTask类继承关系:

并发编程从零开始(十六)-ForkJoinPool_第4张图片

在ForkJoinPool中,对应的接口如下:

并发编程从零开始(十六)-ForkJoinPool_第5张图片


16 核心数据结构

与ThreadPoolExector不同的是,除一个全局的任务队列之外,每个线程还有一个自己的局部队列。

并发编程从零开始(十六)-ForkJoinPool_第6张图片

核心数据结构如下所示:

并发编程从零开始(十六)-ForkJoinPool_第7张图片

下面看一下这些核心数据结构的构造过程:

image-20211103093637628

并发编程从零开始(十六)-ForkJoinPool_第8张图片


17 工作窃取队列

关于上面的全局队列,有一个关键点需要说明:它并非使用BlockingQueue,而是基于一个普通的

你可能感兴趣的:(juc,juc,并发编程)