基本概念:

fork/join框架是java7提供了一个并发执行任务的框架,把一个大任务分割成若干个小任务,小任务汇总得到最终结果。
其中有工作窃取机制,子任务分别放到不同的队列中去,并为每个队列创建一个单独的线程来执行队列里的任务。如果某个队列里任务做完,对应那个线程会去其它队列的尾部拿任务执行,被窃取线程是去这个队列的头部拿任务执行,减少了任务的竞争。

对比TheadPoolExecutor

ForkJoinPool与ThreadPoolExecutor有个很大的不同之处在于,ForkJoinPool存在引入了工作窃取设计。
forkjoinPool适合计算密集型的任务,特别适用于递归问题,在递归问题中,任务需要运行子任务,然后处理它们的结果。(这通常被称为“分而治之”)。
如果存在IO等需要堵塞的任务,还是要用TheadPoolExecutor创建线程池,TheadPoolExecutor通常被用来并发地处理许多独立的请求。ForkJoinPool是ThreadPoolExecutor线程池处理计算密集型场景不足的补充。

学习中提到了线程数数量的设置,参考了《java并发编程的艺术》Nthreads = Ncpu x Ucpu x (1 + W/C),但是实际上生产环境中有其他线程的影响,很难评估任务的计算时间C和等待时长W。另外一张计算方法,核心线程数=tps*time,有时候流程不均匀的情况下,也不是非常合理。并发任务的执行情况和任务类型相关,IO密集型和CPU密集型的任务运行起来的情况差异非常大,但这种占比是较难合理预估的,这导致很难有一个简单有效的通用公式帮我们直接计算出结果。
美团的一篇文章里提出了动态线程池:
动态化线程池的核心设计包括以下三个方面:
简化线程池配置:线程池构造参数有8个,但是最核心的是3个:corePoolSize、maximumPoolSize,workQueue,它们最大程度地决定了线程池的任务分配和线程分配策略。考虑到在实际应用中我们获取并发性的场景主要是两种:(1)并行执行子任务,提高响应速度。这种情况下,应该使用同步队列,没有什么任务应该被缓存下来,而是应该立即执行。(2)并行执行大批次任务,提升吞吐量。这种情况下,应该使用有界队列,使用队列去缓冲大批量的任务,队列容量必须声明,防止任务无限制堆积。所以线程池只需要提供这三个关键参数的配置,并且提供两种队列的选择,就可以满足绝大多数的业务需求,Less is More。
参数可动态修改:为了解决参数不好配,修改参数成本高等问题。在Java线程池留有高扩展性的基础上,封装线程池,允许线程池监听同步外部的消息,根据消息进行修改配置。将线程池的配置放置在平台侧,允许开发同学简单的查看、修改线程池配置。
增加线程池监控:对某事物缺乏状态的观测,就对其改进无从下手。在线程池执行任务的生命周期添加监控能力,帮助开发同学了解线程池状态。

使用

ForkJoinPool中有四个核心参数,
1)parallelism :决定工作线程的数量。如果未设置的话,将使用Runtime.getRuntime().availableProcessors()
2)asyncMode:asyncMode为true时,将使用先进先出队列,而为false时则使用后进先出的模式