ForkJoin 线程池框架回顾
ForkJoin 框架其实就是一个线程池 ExecutorService 的实现,通过工作窃取(work-stealing)算法,获取其他线程中未完成的任务来执行。
可以充分利用机器的多处理器优势,利用空闲的线程去并行快速完成一个可拆分为小任务的大任务,类似于分治算法。
ForkJoin 的目标,就是利用所有可用的处理能力来提高程序的响应和性能。本文将介绍 ForkJoin 框架,源码剖析。
ForkJoinPool 核心类实现
ForkJoin 框架的核心是 ForkJoinPool 类,基于 AbstractExecutorService 扩展。
ForkJoinPool 中维护了一个队列数组 WorkQueue[],每个 WorkQueue 维护一个 ForkJoinTask 数组和当前工作线程。
ForkJoinPool 实现了工作窃取(work-stealing)算法并执行 ForkJoinTask。
ForkJoinPool,所有线程和 WorkQueue 共享,用于工作窃取、任务状态和工作状态同步。
核心属性介绍
ADD_WORKER: 100000000000000000000000000000000000000000000000 -> 1000 0000 0000 0000,用来配合 ctl 在控制线程数量时使用
ctl: 控制 ForkJoinPool 创建线程数量,(ctl & ADD_WORKER) != 0L 时创建线程,也就是当 ctl 的第 16 位不为 0 时,可以继续创建线程
defaultForkJoinWorkerThreadFactory: 默认线程工厂,默认实现是 DefaultForkJoinWorkerThreadFactory
runState: 全局锁控制,全局运行状态
workQueues: 工作队列数组 WorkQueue[]
config: 记录并行数量和 ForkJoinPool 的模式(异步或同步)
ForkJoinTask
status: 任务的状态,对其他工作线程和 pool 可见,运行正常则 status 为负数,异常情况为正数
WorkQueue
qlock: 并发控制,put 任务时的锁控制
array: 任务数组 ForkJoinTask<?>[]
pool: ForkJoinPool,所有线程和 WorkQueue 共享,用于工作窃取、任务状态和工作状态同步
base: array 数组中取任务的下标
top: array 数组中放置任务的下标
owner: 所属线程,ForkJoin 框架中,只有一个 WorkQueue 是没有 owner 的,其他的均有具体线程 owner。
WorkQueue 内部就是 ForkJoinTask
workQueue: 当前线程的任务队列,与 WorkQueue 的 owner 呼应
ForkJoinTask 是能够在 ForkJoinPool 中执行的任务抽象类,父类是 Future,具体实现类有很多,这里主要关注 RecursiveAction 和 RecursiveTask。
RecursiveAction 是没有返回结果的任务
RecursiveTask 是需要返回结果的任务
只需要实现其 compute()方法,在 compute()中做最小任务控制,任务分解(fork)和结果合并(join)。
ForkJoinWorkerThread
ForkJoinPool 中执行的默认线程是 ForkJoinWorkerThread,由默认工厂产生,可以自己重写要实现的工作线程。同时会将 ForkJoinPool 引用放在每个工作线程中,供工作窃取时使用。
pool: ForkJoinPool,所有线程和 WorkQueue 共享,用于工作窃取、任务状态和工作状态同步
workQueue: 当前线程的任务队列,与 WorkQueue 的 owner 呼应
ForkJoinPool 作为最核心的组件,维护了所有的任务队列 WorkQueues,workQueues 维护着所有线程池的工作线程,工作窃取算法就是在这里进行的。
每一个 WorkQueue 对象中使用 pool 保留对 ForkJoinPool 的引用,用来获取其 WorkQueues 来窃取其他工作线程的任务来执行。
同时 WorkQueue 对象中的 owner 是 ForkJoinWorkerThread 工作线程,绑定 ForkJoinWorkerThread 和 WorkQueue 的一对一关系,每个工作线程会优先完成自己队列的任务,当自己队列中的任务为空时,才会通过工作窃取算法从其他任务队列中获取任务。
WorkQueue 中的 ForkJoinTask<?>[] array,是每一个具体的任务,插入 array 中的第一个任务是最大的任务。