- 出现的原因
- 线程池的缺点
- 无法对大任务进行拆分,对于某个任务只能由单线程执行
- 工作线程从队列中获取任务时存在竞争情况
- ForkJoinPool就可以解决这些缺点
- 分治算法与Fork/Join模式
- 在并发计算中,Fork/Join模式往往用于对大任务的并行计算,它通过递归的方式对任务不断地拆解,再将结果进行合并
- 本质是分治算法(Divide-and-Conquer) 的应用
- 分子算法
- 分解:将要解决的问题划分成若干规模较小的同类问题;
- 求解:当子问题划分得足够小时,用较简单的方法解决;
- 合并:按原问题的要求,将子问题的解逐层合并构成原问题的解。

实例 ```java public class TheKingRecursiveSumTask extends RecursiveTask
{ private static final AtomicInteger taskCount = new AtomicInteger(); private final int sumBegin; private final int sumEnd; /** 任务拆分阈值,当任务尺寸大于该值时,进行拆分 */ private final int threshold;
public TheKingRecursiveSumTask(int sumBegin, int sumEnd, int threshold) { this.sumBegin = sumBegin; this.sumEnd = sumEnd; this.threshold = threshold; }
@Override protected Long compute() { if ((sumEnd - sumBegin) > threshold) {
// 两个数之间的差值大于阈值,拆分任务TheKingRecursiveSumTask subTask1 = new TheKingRecursiveSumTask(sumBegin, (sumBegin + sumEnd) / 2, threshold);TheKingRecursiveSumTask subTask2 = new TheKingRecursiveSumTask((sumBegin + sumEnd) / 2, sumEnd, threshold);subTask1.fork();subTask2.fork();taskCount.incrementAndGet();return subTask1.join() + subTask2.join();
} // 直接执行结果 long result = 0L; for (int i = sumBegin; i < sumEnd; i++) {
result += i;} return result; }
public static AtomicInteger getTaskCount() { return taskCount; } }
```
- ForkJoinPool设计与源码分析
描述
ForkJoinPool是Fork/Join模型的实现,ForkJoinPool允许其他线程向它提交任务,并根据设定将这些任务拆分为粒度更细的子任务,这些子任务将由ForkJoinPool内部的工作线程来并行执行,并且工作线程之间可以窃取彼此之间的任务
ForkJoinPool设计
- 在接口实现和继承关系上,ForkJoinPool和ThreadPoolExecutor类似,都实现了Executor和ExecutorService接口,并继承了AbstractExecutorService抽类
- ForkJoinPool主要有两种任务类型
- RecursiveAction
- RecursiveTask

- 四个核心参数
- 并行数(int parallelism):决定工作线程的数量
- 工作线程的创建(ForkJoinWorkerThreadFactory factory):创建线程时,会通过factory来创建
- 异常处理(UncaughtExceptionHandler handler):指定异常处理器,当任务在运行中出错时,将由设定的处理器处理
- 模式指定(boolean asyncMode):asyncMode ? FIFO_QUEUE : LIFO_QUEUE. 当asyncMode为true时,将使用先进先出队列,而为false时则使用后进先出的模式
- 构造函数
- 默认无参构造:ForkJoinPool将根据当前处理器数量来设置并行数量,并使用默认的线程构造工厂(不推荐)
- 通过并行数构造:指定并行数量,以更有效地平衡处理器数量和负载。建议在设置时,并行级别应低于当前处理器的数量
- 自定义全部参数构造:建议你使用这种构造方式
- 总结
- 优点:
- 任务切分:将大的任务分割成更小粒度的小任务,让更多的线程参与执行;
- 任务窃取:通过任务窃取,充分地利用空闲线程,并减少竞争。
- 适用范围:
- 纯函数计算类型
