深入
- ForkJoin的线程池是如何调度子任务的
应用场景
如果一个应用程序能够被分解成多个子任务,而且结合多个子任务的结果就能够得到最终的答案,那么它就适合使用FORK/JOIN模式来实现。
将一个大任务分割成一个个小任务,小任务可以独立执行,彼此之间互相不影响,小任务的执行结果可以合并为总的结果。
分割出来的小任务会放在一个双端队列中,线程从中读取任务出来执行,当然每个线程是对应一个双端队列的,之所以是使用双端队列,是为了实现“工作窃取”,空闲的线程可以从其他队列中窃取任务来执行。
双端队列
为什么会出现双端队列?
工作密取 模式 对比传统的 生产者-消费者 模式,更为灵活,因为多个线程不会因为在同一个工作队列中抢占内容发生竞争。在大多数时候,它们只是访问自己的双端队列。即使需要访问另一个队列时,也是从 队列的尾部获取工作,降低了队列上的竞争程度。
最显著的就是降低线程的等待和竞争。
递归任务RecursiveTask
不断拆分任务的过程是一个递归的过程,递归条件是最终想要执行的最小粒度的任务。
ForkJoinPool
提供给RecursiveTask的线程池框架
跟一般的ExecutorService
有什么区别
实现了工作窃取算法,RecursiveTask只是定义了每个任务的执行逻辑,但是怎么调度这些任务的执行是通过线程池来实现的。它管理工作者线程,并且提供任务的执行状态信息和执行信息
RecursiveTask
会在代码层面不断提交一堆拆分任务的逻辑,拆分出来的子任务通过调用fork方法告诉线程池,你去给我按照双端队列的工作窃取算法来执行一堆的子任务
代码层面
写一个RecursiveTask的子类,就是实现自己的递归逻辑,包括递归出口和递归体,拆分成一堆的子任务,具体子任务怎么有效的执行,是ForkJoinPool线程池去调度的。