简介
线程池,分治,工作密取。
几个角色
ForkJoinTask
有3个实现,分别是RecursiveTask,RecursiveAction,CountedCompleter.
RecursiveTask
可以递归执行的ForkJoinTask。
RecursiveAction
无返回值的RecursiveTask。
CountedCompleter
任务执行完成后,触发执行自定义钩子函数。
ForkJoinWorkerThread
运行 ForkJoinTask 任务的工作线程。
WorkQueue
任务队列,支持LIFO(last-in-first-out)的push和pop操作(top端),和FIFO(first-in-first-out)的poll操作(base端),队栈二相性。
WorkQueue[]
ForkJoinPool 中的任务分为两种,一种是本地提交的任务Submission task,通过execute()、submit()等方法提交的任务;另外一种是 fork出的子任务Worker task.
两种任务都会存放在WorkQueue数组中,Submission task存放在WorkQueue数组的偶数索引位置,Worker task存放在奇数索引位置。
基本算法
1 protected Long compute() { 2 if (end - start <= THRESHOLD) { 3 return justCompute(); 4 } else { 5 left.fork(); 6 right.fork(); 7 return right.join() + left.join(); 8 } 9 }
源码解析
数据结构
ForkJoinWorkerThreadFactory
线程工厂接口,用于创建工作线程ForkJoinWorkerThread,默认实现是DefaultForkJoinWorkerThreadFactory.
WorkQueue
work-stealing 模式的双端任务队列(内部是数组实现,ForkJoinTask<?>[] array)。
-
工作线程调用fork()方法将分解的任务入队(栈),处于top端(栈顶),工作线程处理自己工作队列的任务时,从栈顶取任务。
- 工作线程也会窃取别的队列的任务,从base端获取。
内部属性
1 static final int INITIAL_QUEUE_CAPACITY = 1 << 13; // 初始队列容量 2 static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 最大队列容量 3 volatile int scanState; // 扫描状态, <0: inactive; 奇数:scanning; 偶数:running 4 int stackPred; // 前任池(WorkQueue[])索引,由此构成一个栈 5 int nsteals; // 偷取的任务个数 6 int hint; // 记录偷取者的索引 7 int config; // pool index | mode 8 volatile int qlock; // 1: locked, < 0: terminate; else 0 9 volatile int base; // 栈底/队列头 10 int top; // 栈顶/队列尾 11 ForkJoinTask<?>[] array; // 任务数组 12 final ForkJoinPool pool; // the containing pool (may be null) 13 final ForkJoinWorkerThread owner; // 当前工作队列的工作线程,共享模式下为null 14 volatile Thread parker; // 调用park阻塞期间为owner,其他情况为null 15 volatile ForkJoinTask<?> currentJoin; // 记录当前join来的任务 16 volatile ForkJoinTask<?> currentSteal; // 记录从其他工作队列偷取过来的任务
EmptyTask
空任务,用于替换队列中join的任务。
前置条件,subTask之前已经通过fork入队(当前线程所属的工作队列/栈)
线程执行当前任务时,碰见subTask.join(),便会去队列里查找subTask,从栈顶开始,如果恰巧栈顶就是subTask,那么直接执行;若是在队列中间找到,我们知道,任务队列内部是数数组(ForkJoinTask<?>[]),所以没法使其他的任务填补这个空缺(若不填补,此位置为null,那么每个任务便多了一个非空判断),所以使用一个EmptyTask填补此位置(当线程拿到这个task时,什么都不用做,因为方法体没内容,索引很快执行下一个任务)。
核心参数
关键方法
行文至此结束。
尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_fjp.html