深入理解-CPU核心数与线程池并发线程数关系

那是一个风和日丽的下午!

面试官微微一笑,对我说:“小伙子,合理配置线程池你是如何考虑的?

我微微一笑,说出了我的答案:

首先确认业务是CPU密集型还是IO密集型的,

如果是CPU密集型的,那么就应该尽量少的线程数量,一般为CPU的核数+1;

如果是IO密集型:所以可多分配一点 cpu核数*2 也可以使用公式:CPU 核数 / (1 - 阻塞系数);其中阻塞系数在 0.8 ~ 0.9 之间。

面试官心想,有点东西,接着追问:“那你是如何得到的这个公式,或者说你是如何根据CPU核心数确定线程池并发线程数的

我心想这不是小瞧我嘛?不屑回答的我脱下了我的帽子

哈哈哈哈,进入正题!

扫描二维码关注公众号,回复: 7812713 查看本文章

1,追书溯源

关于如何计算并发的线程数量,一般分两派,来自两本书,且都是好书,

第一派:《Java Concurrency in Practice》即《java并发编程实践》,我们简称A派。

第二派:《Programming Concurrency on the JVM Mastering》即《Java 虚拟机并发编程》,我们简称B派。

到底哪个是对的?我们截取书中部分内容具体看看。

A派一书中是这样写的

B派一书中是这样写的

2,大胆分析

对于A派,假设CPU跑满,即撇开CPU使用率这个因素,A派认为 线程数 = Ncpu * ( 1 + w/c )

大胆假设将B派的公式等于A派公式,

Ncpu / (1-阻塞系数) = Ncpu * ( 1 + w/c )阻塞系数 = 阻塞时间 /(阻塞时间+计算时间)

这个结论在派系二后续中得到应征,如下图:

\color{red}{所以得出一个结论:A派和B派其实是一个公式!}

3,实际运用

那么实际使用中并发线程数如何设置呢?分析如下(我们以A派公式为例):

Nthreads = Ncpu * (1+w/c)

IO密集型:一般情况下,如果存在IO,那么肯定w/c>1(阻塞耗时一般都是计算耗时的很多倍),但是需要考虑系统内存有限(每开启一个线程都需要内存空间),这里需要上服务器测试具体多少个线程数适合(CPU占比、线程数、总耗时、内存消耗)。

如果不想去测试,保守点取1即,Nthreads=Ncpu*(1+1)=2Ncpu。这样设置一般都OK。

CPU密集型: 假设没有等待w=0,则W/C=0. Nthreads=Ncpu。

再次归纳下:

IO密集型 = 2Ncpu(可以测试后自己控制大小,2Ncpu一般没问题)(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)

CPU密集型 = Ncpu(常出现于线程中:复杂算法)

java中:Ncpu = Runtime.getRuntime().availableProcessors()

4,继续研究

A派的另一个说法

即对于计算密集型的任务,在拥有N个处理器的系统上,当线程池的大小为N+1时,通常能实现最优的效率。(即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保CPU的时钟周期不会被浪费。)

即,计算密集型=Ncpu+1,但是这种做法导致的多一个cpu上下文切换是否值得,这里不考虑。读者可自己考量。


本文借鉴归纳:Flag Counter 《根据CPU核心数确定线程池并发线程数》

猜你喜欢

转载自juejin.im/post/5dc6a548e51d45660e6b1e01