线程池参数配置

线程池的任务调度机制

在介绍线程池的参数配置之前,我们先看下线程池的任务调度机制。

任务调度是线程池的主要入口,当用户提交了一个任务,接下来这个任务将如何执行都是由这个阶段决定的。了解这部分就相当于了解了线程池的核心运行机制。
首先,所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

image.png

参数的合理配置

方案一

在网上有几种使用的人数比较多的参数配置方案。
image.png
这是方案一,《java并发编程实践》给出的线程池配置
Ncpu: cpu核数
Ucpu:目标cpu利用率
W/C : 等待时间与计算时间的比例
Nthreads: 使池子保持在最佳转态的线程数。
缺点:这个方案的缺点很明显,开发人员很难去计算等待时间与计算时间的比例。实现难度较高。

方案二

java开发人员会经常在csdn、博客园等网站检索到下面这种配置
coreSize = 2 cpu核数 (如果是cpu密集型的话,这个数是cpu核数+1)
maxSize = 25
cpu核数
缺点:正常来说,我们的程序里面会有多个线程池,最少也是2个,一个tomcat线程池,一个执行异步方法的线程池。故这样配置明显不符合实际场景。

综上,对于线程池配置的并没有银弹。我们可能需要根据不同的场景去设置线程池。

动态线程池

通过上面对线程池参数的探究,我们得出并没有线程池参数并没有固定的配置公式,需要我们根据各自场景去单独设置。那么每次修改线程池都需要修改代码,申请上线部署,观察,如果参数没达到预期的效果,又需要重新上线一次,这样子无疑给我们的开发同学和运维同学带来不必要的麻烦。

幸运的是,java线程池在设计时,变支持动态的修改线程池的参数。我们可以通过配置中心,来便捷的修改线程池。
image.png