1. 核心线程数(corePoolSize)
核心线程数是线程池在空闲时仍然保留的线程数量
cpu密集型是为了高效
io密集型是为了更多的线程能够io
如何设置:
CPU密集型任务:
如果任务主要消耗CPU资源(例如复杂计算),核心线程数应接近CPU核心数或稍低。
推荐公式:<strong><span class="ne-text">corePoolSize = CPU核心数 + 1</span></strong>
理由:线程数等于CPU核心数可以最大化利用CPU,同时加上1是为了处理偶尔的上下文切换或其他轻量任务。
IO密集型任务:
如果任务涉及大量的IO操作(如网络请求、数据库操作、文件读写),线程会因IO等待而阻塞,可以使用更多的线程以提高吞吐量**。**
推荐公式:<strong><span class="ne-text">corePoolSize = CPU核心数 * 2</span></strong> 或 <span class="ne-text">CPU核心数 / (1 - 阻塞系数)</span>
阻塞系数 = IO等待时间 / (IO等待时间 + CPU计算时间)。例如,如果阻塞系数是0.8,则<span class="ne-text">corePoolSize ≈ CPU核心数 * 5</span>。
混合型任务:
如果任务既有CPU密集部分又有IO操作,需要分析两部分的比例并综合考虑,或将混合任务分解到不同线程池中分别处理。
额外建议:
如果任务需要快速响应,可以适当提高<span class="ne-text">corePoolSize</span>,确保有足够的线程来处理任务。
根据实际场景,可以通过压测调整,找到最优核心线程数。
2. 最大线程数(maximumPoolSize)
最大线程数是线程池能容纳的最大线程数量,当任务队列已满时会创建非核心线程
如何设置:
最大线程数一般是核心线程数的2~5倍。
任务突增: 如果业务可能出现流量高峰(例如秒杀、抢购场景),可以设置一个较高的最大线程数来承载突增流量。
有限资源保护: 如果系统资源有限,不建议设置过高,防止线程过多导致上下文切换或OOM(内存溢出)。
回收策略: 非核心线程会在<span class="ne-text">keepAliveTime</span>后被回收,因此最大线程数只在任务爆发时起作用
3.阻塞队列
默认为有界队列
| 队列类型 | 特性 | 适用场景 | JDK实现类 |
|---|---|---|---|
| 无界队列 | 理论上可以无限增长 | 任务提交速度波动大,允许短暂积压 | <strong><span class="ne-text">LinkedBlockingQueue</span></strong> |
| 有界队列 | 固定容量,队列满时触发拒绝策略 | 需要防止资源耗尽 | <strong><span class="ne-text">ArrayBlockingQueue</span></strong> |
| 同步移交队列 | 不存储元素,每个插入操作必须等待对应的移除操作 | 高吞吐量场景,快速传递任务 | <strong><span class="ne-text">SynchronousQueue</span></strong> |
| 优先级队列 | 按优先级排序任务 | 需要任务优先级调度 | <strong><span class="ne-text">PriorityBlockingQueue</span></strong> |
| 延迟队列 | 元素在指定延迟时间后才能被取出 | 定时任务调度 | <strong><span class="ne-text">DelayQueue</span></strong> |
| 双端队列 | 可以从两端插入和移除元素 | 工作窃取模式 | <strong><span class="ne-text">LinkedBlockingDeque</span></strong> |
-
CPU 密集型任务**:**
- 阻塞队列长度:
<span class="ne-text">CPU 核心数 * 2</span>
- 阻塞队列长度:
-
IO 密集型任务**:**
- 阻塞队列长度:
<span class="ne-text">CPU 核心数 * 10</span>
- 阻塞队列长度:
3.2 基于 QPS 和任务处理时间的公式
- QPS(每秒查询率)****:假设系统每秒处理的任务数。
- 任务处理时间**:每个任务的平均处理时间(秒)。**
- 阻塞队列长度**:**
<span class="ne-text">QPS * 任务处理时间 * 冗余系数</span>
例如:
- 假设系统 QPS 为 1000,任务处理时间为 0.1 秒,冗余系数为 2.0。
- 阻塞队列长度:
<span class="ne-text">1000 * 0.1 * 2.0 = 200</span>
4业务
线程池调优主要要根据实际业务场景去进行调整,一般调整参数主要是核心线程数、最大线程数、阻塞队列以及拒绝策略·如何设置核心线程数,可以从响应时间和吞吐量敏感度考虑
·对于C端场景需要考虑响应时间,对于批量调用下游服务的场景,可以合理配置核心线程数让任务尽早的执行完。hystrix官网建议,核心线程数=下游任务QPS*下游任务TP99+余线程数Buffer
**·**对于B端场景需要考虑吞吐量,对于使用MQ、Job进行任务处理时,由于不考虑立刻响应,核心线程数够用即可,可以让尽可能多的任务阻塞在队列中,少占用CPU资源·对于最大线程数,通常情况下不要超过CPU核心数
因为超过了可能达不到提高系统的并发能力,反而会增加线程切换的开销,可以根据系统的负载情况和任务的处理时间,可以预估出系统需要的最大线程数。另外对于线程池也需要考虑加监控(比如告警),监控任务执行时间、活跃线程数、队列中任务数等,用于上线评估,最好可以核心线程数可配置化来应对大促,
对于拒绝策略可以根据业务、监控情况来设计
**·对于一些比较核心的业务,会在大促前进行相关压测,我们通过压测来观测接口平均点在哪里,如果是下游的问题,就需要让产品协调资源去进行处理,次如果是我们接口的问题,就需要对核心业务的线程池核心参数进行合理配置,**一般可以先基于过往的监控情况,先配置预估的值,如果流量激增,我们这边就需要通过线程池的监控进行分析
·对于线程池的监控,执行任务的时候可以将当前线程池对象的参数打印出来,然后通过三方平台将日志解析出来,通过数据看板的形式来监控,如果发现问题,就及时告警出来
·其次就是我们可以借鉴美团动态线程池的设计思想,通过JDK提供的几个核心的设置方法通过不同策略去进行动态调整,可以做成一个队列积压区间,动态调整线程数,同时也要定期上报指标,此外也可以根据不同项目的指标情况,通过机器学习进行定制化策略