Fork/Join框架
定义:java并发工具包中一种可以将大任务拆分为很多小任务异步执行的工具.
模块:
任务对象:ForkJoinTask(RecursiveTask,RecursiveAction,CountedCompleter这三个都是ForkJoinTask的子类)
RecursiveTask:是一个可以递归执行的ForkJoinTask
RecursiveAction:是一个无返回值的RecursiveTask
CountedCompleter:在任务执行完毕后会触发一个自定义的钩子函数
执行Fork/Join任务的线程:ForkJoinWorkerThread
线程池:ForkJoinPool
这三者关系:ForkJoinPool可以通过池中的ForkJoinWorkerThread来处理ForkJoinTask任务.
核心思想:分治算法(Divide-and-Conquer)
把任务递归的拆分为各个子任务,这样可以更好的利用系统资源,尽可能的使用所有可用的计算能力来提升应用性能.
核心思想:work-stealing(工作窃取)算法
线程池内的所有工作线程都尝试找到并执行已经提交的任务,或者是被其他活动任务创建的子任务(如果不存在就阻塞等待).这种特性使得ForkJoinPool在运行多个可以产生子任务的任务,或者是提交的许多小任务时效率更高.尤其是构建异步模型的ForkJoinPool时,对不需要合并的事件类型任务也非常适用.
在ForkJoinPool中,线程池中每个工作线程(ForkJoinWorkerThread)都对应一个任务队列,工作线程优先处理来自自身队列的任务(LIFO或FIFO顺序,参数mode决定),然后以FIFO的顺序随机窃取其他队列中的任务.
具体思路如下:
每个线程都有一个自己的WorkQueue,该工作队列是一个双端队列
队列支持三个功能push,pop,poll
push/pop只能被队列的所有者线程调用,而poll可以被其他线程调用
划分的子任务调用fork时,都会被push到自己的队列中.
默认情况下,工作线程从自己的WorkQueue获取任务并执行.
当自己的WorkQueue为空时,线程随机从另一个线程队列末尾调用poll方法窃取任务.
Fork/Join框架的执行流程
上图可以看出ForkJoinPool中的任务执行分为两种:
直接通过FJP提交的外部任务(external/submissions task),存放在workqueues的偶数槽位.
通过内部fork分割的子任务(Worker task),存放在workQueues的奇数槽位.