Fork/Join框架流程梳理

Dcr 1年前 ⋅ 827 阅读

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的奇数槽位.

全部评论: 0

    我有话说: