线程池调优
调优指标
线程池的大小依赖于所执行任务的特性以及程序运行的环境,线程池的大小应该应采取可配置的方式(写入配置文件)或者根据可用的Runtime.availableProcessors()
来进行设置,其中0≤ Ucpu ≤1
;
-
对于对于纯
CPU 计算的任务- 即不依赖阻塞资源(外部接口调用)以及有限资源(线程池)的CPU 密集型(compute-intensive)任务线程池的大小可以设置为:Nthreads = Ncpu+1
。 -
如果执行的任务除了
cpu 计算还包括一些外部接口调用或其他会阻塞的计算,那么线程池的大小可以设置为Nthreads = Ncpu - Ucpu -(1 + W / C)
。可以看出对于IO 等待时间长于任务计算时间的情况,W/C
大于1 ,假设cpu 利用率是100% ,那么W/C
结果越大,需要的工作线程也越多,因为如果没有足够的线程则会造成任务队列迅速膨胀。 -
如果任务依赖于一些有限的资源比如内存,文件句柄,数据库连接等等,那么线程池最大可以设置为
Nthreads ≤ Rtotal/Rper
。
单线程
单线程情况下,服务接收到请求后开始初始化,资源准备,计算,返回结果,时间主要花在
RT = T(cpu) + T(io)
QPS = 1000ms / RT
多线程
单线程情况很好计算,多线程情况就复杂了,我们目标是计算出最佳并发量,也就是线程数
- 单核情况:
N = [T(cpu) + T(io)] / T(cpu)
M 核情况:N = [T(cpu) + T(io)] / T(cpu) * M
由于多核情况N = [T(cpu) + T(io)] / T(cpu) * M * P
。
吞吐量
我们知道单线程的
QPS = 1000ms / RT * N = 1000ms / [T(cpu) + T(io)] - [T(cpu) + T(io)] / T(cpu) * M * P= 1000ms / T(cpu) * M * P
在机器核数固定情况下,也即是并发模式下最大的吞吐量跟服务的
前面我们是假设机器核数固定的情况下做优化的,那假如我们把缓存,IO,锁都优化了,剩下的还有啥空间去突破呢?回想一下我们谈基础理论的时候提到的