合理选择线程池大小

在看这个问题之前一定要明白的是线程(应用程序)是不能控制CPU的计算资源分配的。他只是将任务提交给操作系统,至于CPU资源如何分配是由操作系统来控制的。

对于多线程下的解决方案,并不是线程量越多就越好,线程池数量的设置要考虑到很多方面,首先要确认这个线程池所处理的任务是属于CPU密集型还是IO密集型或者属于混合型。对于CPU密集型一般线程池数量为CPU核数+1,+的这个1是充分利用CPU(如当某个线程出现空闲时或一个线程计算结束切换新任务时能够充分利用这部分资源),而对于IO密集型,一般设置为2N(CPU数)+1.但是这并不是绝对的,要考虑到IO的响应时间,如果响应时间比较长会导致CPU空闲的情况可以适当增加线程数。但即使是这种情况也并不是可以无限制的增大,因为还要考虑到IO的压力。如针对数据库,如果有过多的线程同时连接数据库使得数据库连接数过多会增大数据库的压力。这个在设置线程池大小时也要考虑进去。

对线程数量新的理解:

纯计算型

CPU密集型所谓的N+1根本没有意义。如果是纯计算型,那么N个CPU就能把CPU打满,多加了这个1反而可能会导致线程切换(应用中仅有N个线程)或者是提升了线程切换的概率(应用还有其他线程或进程)。

这个+1常见的说法就是上面我曾经的理解: 如当某个线程出现空闲时或一个线程计算结束切换新任务时能够充分利用这部分资源。   

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

但是这个说法,并不完全对,首先,线程执行过程中因为业务异常或中断退出,那么这个线程会在final块内执行processWorkerExit方法,这个方法会重新添加一个非核心线程到池子中(这个可能导致在任务队列没满的时间,线程量就大于了核心线程,比如我这边还没添加成功时,一个新任务被提交,添加了一个核心线程,然后这边在执行异常补充线程流程)。这个过程中CPU依然被完全占用的, 并不存在所谓的替补线程能替补上来的情况。

另外一种情况是线程是被kill掉的情况,那么线程池会在任务提交时符合条件的情况下重新建立线程。 而线程kill到任务提交满足条件重新创建的线程过程确实可能造成一个U被浪费(前提是你的应用除了线程池线程,其他线程(包括任务提交线程)都处于非计算状态(比如阻塞或者IO)),否则空出来的这个U也会被其他线程池外的任务利用起来。

混合型

对于混合型,2N+1太过粗暴了,根本没考虑混合型的利用情况,而是假设CPU占据任务总时间的50%。

实际的计算公式可以用这个公式估算: 

估计线程量   = (CPU数/(任务CPU运算时间/任务总时间))*你期望的CPU利用率百分比

或者是

估计线程量   = CPU数*(任务总时间/任务CPU运算时间)*你期望的CPU利用率百分比

两个公式一样,看哪个容易理解。

但是注意,这只是估算,要想获得最佳的值,根据业务调整,或者提前进行压测获得才是正道。

为什么混合型考虑了CPU利用率百分比而密集型不考虑呢?这是因为混合型任务多于CPU,可以通过任务切换把计算压力分散一下,通过空一些时间片而控制利用率。而密集型则不行,对于密集型,即使任务数低于CPU,那么也不会产生切换,而是拿到任务的CPU一直执行,比如说4个CPU, 但是只设置了3个线程,那么就会有3个CPU一直打满,一个CPU完全空闲。而不会说一个CPU各算一段这样。

线程池的拒绝策略是指当线程池已经达到最大工作线程数但仍接收到新的任务时,采取的不同处理方式。常见的几种拒绝策略包括: 1. **AbortPolicy**(默认策略):直接抛出RejectedExecutionException异常,终止当前线程并回滚任务到队列头部。 2. **CallerRunsPolicy**:让调用者(通常主线程)继续运行新任务,而不是放入线程池。这种方式可能导致主线程阻塞,适用于那些不需要并发执行的任务。 3. **DiscardOldestJobPolicy**:丢弃线程池中最早的任务,然后接受新的任务,适用于优先级较高的任务可以随时添加的情况。 4. **DiscardPolicy** 或 **DiscardPolicyFactory**:直接丢弃新任务,不会抛出异常,适合资源有限、任务瞬息万变的场景。 合理分配线程池大小主要考虑以下几个因素: - **CPU核心数**:通常线程数不应超过CPU的核心数,因为过多会增加上下文切换开销。 - **并发任务量**:根据应用预期同时处理的请求量设置。 - **任务性质**:I/O密集型任务可以适当多设,CPU密集型任务应少设避免过度竞争。 - **预留空间**:为了应对突发流量,可以留出一部分空闲线程。 - **吞吐量和响应时间**:需要权衡在保证系统性能的同时,提供足够的快速响应。 要确定具体的线程池大小,可以通过监控系统的负载和调整来优化,或者采用动态调整的算法如Hystrix、Spring ThreadPool等工具。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yue_hu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值