前沿

大家都用过线程池,但是线程池数量设置为多少比较合理呢?

线程数的设置的最主要的目的是为了充分并合理地使用 CPU 和内存等资源,从而最大限度地提高程序的性能,因此让我们一起去探索吧!

首先要考虑到 CPU 核心数,那么在 Java 中如何获取核心线程数?

可以使用 Runtime.getRuntime().availableProcessor() 方法来获取(可能不准确,作为参考)
或者直接去服务器查看

温故为什么使用线程

场景

如果有两个任务需要处理,一个任务A,一个任务B

方案一:一个线程执行任务A和B,A执行完后,执行B
方案二:两个线程A和B去执行任务A 和 B,同时进行

哪个方案会快点?应该很多人会回答,肯定是方案二啊,多线程并行去处理任务A和B,肯定快啊。是这样吗?回答这个问题之前,先带着大家去回顾梳理一下。

线程执行

线程的执行,是由CPU进行调度的,一个CPU在同一时刻只会执行一个线程,我们看上去的线程A 和 线程B并发执行。

为了让用户感觉这些任务正在同时进行,操作系统利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。

上下文切换过程是需要时间的;现在我们来看一下上面的问题,小伙伴们再看一下是哪个方案快呢?是不是有些小伙伴们会说方案一,因为不需要线程切换;方案二需要来回切换这两个线程,耗时会多点。

小伙伴们心中此时是不是会有疑惑,那为什么会有多线程?先不急,再往下看。

为什么要使用多线程

小伙伴想想在我们真实业务中,我们是什么流程?

上图的流程:

1、先发起网络请求

2、Web服务器解析请求

3、请求后端的数据库获取数据

4、获取数据后,进行处理

5、把处理结果放回给用户

这个是我们处理业务的时候,常规的请求流程;我们看一下整个过程涉及到什么计算机处理。

1、网络请求----->网络IO

2、解析请求----->CPU

3、请求数据库----->网络IO

4、MySQL查询数据----->磁盘IO

5、MySQL返回数据----->网络IO

6、数据处理----->CPU

7、返回数据给用户----->网络IO

讲到这里,小伙伴们是不是感觉又不乱了,在真实业务中我们不单单会涉及CPU计算,还有网络IO和磁盘IO处理,这些处理是非常耗时的。如果一个线程整个流程是上图的流程,真正涉及到CPU的只有2个节点,其他的节点都是IO处理,那么线程在做IO处理的时候,CPU就空闲出来了,CPU的利用率就不高。

小伙伴们现在知道多线程的用处了吧,对,就是为了提升CPU利用率。

提升QPS/TPS

衡量系统性能如何,主要指标系统的(QPS/TPS)

QPS/TPS:每秒能够处理请求/事务的数量

并发数:系统同时处理的请求/事务的数量

响应时间:就是平均处理一个请求/事务需要时长

QPS/TPS = 并发数/响应时间

上面公式代表并发数越大,QPS就越大;所以很多人就会以为调大线程池,并发数就会大,也会提升QPS,所以才会出现一开始前言所说的,大多数人的误区。

其实QPS还跟响应时间成反比,响应时间越大,QPS就会越小。

虽然并发数调大了,就会提升QPS,但线程数也会影响响应时间,因为上面我们也提到了上下文切换的问题,那怎么设置线程数的呢?

如何设置线程数

那我们如何分配线程?我们提供一个公式:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

备注:这个公式也是前辈们分享的,当然之前看了淘宝前台系统优化实践的文章,和上面的公式很类似,不过在CPU数目那边,他们更细化了,上面的公式只是参考。不过不管什么公式,最终还是在生产环境中运行后,再优化调整。

我们继续上面的任务,我们的服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 4 = 20。也就是设置20个线程数最佳。

从这个公式上面我们就得出,线程的等待时间越大,线程数就要设置越大,这个正好符合我们上面的分析,可提升CPU利用率。那从另一个角度上面说,线程数设置多大,是根据我们自身的业务的,需要自己去压力测试,设置一个合理的数值。

基础常规标准

在确认了核心数后,再去判断是 CPU 密集型任务还是 IO 密集型任务:
CPU 密集型任务:
比如像加解密,压缩、计算等一系列需要大量耗费 CPU 资源的任务,大部分场景下都是纯 CPU 计算。
IO 密集型任务:
比如像 MySQL 数据库、文件的读写、网络通信等任务,这类任务不会特别消耗 CPU 资源,但是 IO 操作比较耗时,会占用比较多时间。

1、CPU密集型:操作内存处理的业务,一般线程数设置为:CPU核数 + 1 或者 CPU核数*2。核数为4的话,一般设置 5 或 8

2、IO密集型:文件操作,网络操作,数据库操作,一般线程设置为:cpu核数 / (1-0.9),核数为4的话,一般设置 40
在知道如何判断任务的类别后,让我们分两个场景进行讨论:

CPU 密集型任务

对于 CPU 密集型计算,多线程本质上是提升多核 CPU 的利用率,所以对于一个 8 核的 CPU,每个核一个线程,理论上创建 8 个线程就可以了。

如果设置过多的线程数,实际上并不会起到很好的效果。此时假设我们设置的线程数量是 CPU 核心数的 2 倍,因为计算任务非常重,会占用大量的 CPU 资源,所以这时 CPU 的每个核心工作基本都是满负荷的,而我们又设置了过多的线程,每个线程都想去利用 CPU 资源来执行自己的任务,这就会造成不必要的上下文切换,此时线程数的增多并没有让性能提升,反而由于线程数量过多会导致性能下降。

因此,对于 CPU 密集型的计算场景,理论上线程的数量 = CPU 核数就是最合适的,不过通常把线程的数量设置为CPU 核数 +1,会实现最优的利用率。即使当密集型的线程由于偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费,从而保证 CPU 的利用率。

如下图就是在一个 8 核 CPU 的电脑上,通过修改线程数来测试对 CPU 密集型任务(素数计算)的性能影响。

可以看到线程数小于 8 时,性能是很差的,在线程数多于处理器核心数对性能的提升也很小,因此可以验证公式还是具有一定适用性的。

除此之外,我们最好还要同时考虑在同一台机器上还有哪些其他会占用过多 CPU 资源的程序在运行,然后对资源使用做整体的平衡。

IO 密集型任务

对于 IO 密集型任务最大线程数一般会大于 CPU 核心数很多倍,因为 IO 读写速度相比于 CPU 的速度而言是比较慢的,如果我们设置过少的线程数,就可能导致 CPU 资源的浪费。而如果我们设置更多的线程数,那么当一部分线程正在等待 IO 的时候,它们此时并不需要 CPU 来计算,那么另外的线程便可以利用 CPU 去执行其他的任务,互不影响,这样的话在任务队列中等待的任务就会减少,可以更好地利用资源。

对于 IO 密集型计算场景,最佳的线程数是与程序中 CPU 计算和 IO 操作的耗时比相关的,《Java并发编程实战》的作者 Brain Goetz 推荐的计算方法如下:

线程数 = CPU 核心数 * (1 + IO 耗时/ CPU 耗时)

通过这个公式,我们可以计算出一个合理的线程数量,如果任务的平均等待时间长,线程数就随之增加,而如果平均工作时间长,也就是对于我们上面的 CPU 密集型任务,线程数就随之减少。可以采用 APM 工具统计到每个方法的耗时,便于计算 IO 耗时和 CPU 耗时。

在这里引用Java并发编程实战中的图,方便大家更容易理解:

还有一派的计算方式是《Java虚拟机并发编程》中提出的:

线程数 = CPU 核心数 / (1 - 阻塞系数)

其中计算密集型阻塞系数为 0,IO 密集型阻塞系数接近 1,一般认为在 0.8 ~ 0.9 之间。比如 8 核 CPU,按照公式就是 2 / ( 1 - 0.9 ) = 20 个线程数

上图是 IO 密集型任务的一个测试,是在双核处理器上开不同的线程数(从 1 到 40)来测试对程序性能的影响,可以看到线程池数量达到 20 之后,曲线逐渐水平,说明开再多的线程对程序的性能提升也毫无帮助。

太少的线程数会使得程序整体性能降低,而过多的线程也会消耗内存等其他资源,所以如果想要更准确的话,可以进行压测,监控 JVM 的线程情况以及 CPU 的负载情况,根据实际情况衡量应该创建的线程数,合理并充分利用资源。

同时,有很多线程池的应用,比如 Tomcat、Redis、Jdbc 等,每个应用设置的线程数也是不同的,比如 Tomcat 为流量入口,那么线程数的设置可能就要比其他应用要大。

总结

通过对线程数设置的探究,我们可以得知线程数的设置首先和 CPU 核心数有莫大关联,除此之外,我们需要根据任务类型的不同选择对应的策略,具体的怎么设置要根据业务上需要/服务器的环境/QPS/TPS等指标等等有关系。
线程的平均工作时间所占比例越高,就需要越少的线程;线程的平均等待时间所占比例越高,就需要越多的线程;增强cpu的使用率。
针对不同的程序,进行对应的实际测试就可以得到最合适的选择。

多线程线程数设置多少合适相关推荐

  1. 线程池核心数与最大线程数设置

    线程池核心数与最大线程数设置 总结: 核心线程 CPU密集型:核心线程数=CPU核心数(或 核心线程数=CPU核心数+1) I/O密集型:核心线程数=2*CPU核心数(或 核心线程数=CPU核心数/( ...

  2. 线程池——核心线程数设置依据

    根据线程数设置依据 最大线程数:原则上就是性能最高线程数,因为此时性能已经是最高,再设置比他大的线程数反而性能变低.极端情况下才会使用到最大线程数,正常情况下不应频繁出现超过核心线程数的创建. 核心线 ...

  3. java 多线程 cpu核数_java线程数设置和系统cpu的关系

    这里的cpu个数不是指系统的cpu总个数,也不是指cpu总核心数,而是指cpu的总逻辑处理单元即超线程的个数. IO密集型程序(如数据库数据交互.文件上传下载.网络数据传输等等)设置线程数为2倍的总逻 ...

  4. 【Pytorch】物理cpu、逻辑cpu、cpu核数、pytorch线程数设置

    上周末写ddp,常常遇到中途退出的问题,解决中途遇到了很多CPU线程数和核心数的问题,记录如下 1. 物理cpu.逻辑cpu.cpu核数.超线程 这一部分主要来自什么是物理cpu,什么是逻辑cpu,什 ...

  5. 线程池详解和线程池核心数与最大线程数设置

    ThreadPool 为什么要用线程池 1.创建/销毁线程需要消耗系统资源,线程池可以复用已创建的线程. 2.控制并发的数量.并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃.(主要原因)可以 ...

  6. 压测接口线程数设置_ZAT掌门性能压测巡检系统实战和落地

    项目背景 随着业务拓展,对于接口性能的要求也在上升,各部门也开始针对部分慢接口进行优化,从测试角度针对这些优化需求进行测试时不仅要保证对应接口的功能正常使用同时也要验证接口优化成果.在日常的开发工作中 ...

  7. 多线程线程数经验公式

    一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数) 如果是CPU密集型应用,则线程池大小设置为N+1 如果是IO密集型应用,则线程池大小设置为2N+1(因为io读数据或者缓存的 ...

  8. weblogic9修改线程数设置

    base_domain ---Environment -----Work Managers --------New ------------------ Create a New Work Manag ...

  9. burp爆破线程设置多少_多线程到底需要设置多少个线程?

    我们在使用线程池的时候,会有两个疑问点: 线程池的线程数量设置过多会导致线程竞争激烈 如果线程数量设置过少的话,还会导致系统无法充分利用计算机资源 那么如何设置才不会影响系统性能呢?其实线程池的设置是 ...

最新文章

  1. 机器学习面试题集 - 如何进行 A/B 测试
  2. c#中嵌入echarts_利用c#+jquery+echarts生成统计报表(附源代码)
  3. Unity Shader-描边效果
  4. Java学习笔记-7.Java IO流
  5. TSP - 状态压缩dp
  6. 第三周项目5-数组作数据成员
  7. 动态添加html元素绑定事件,关于javascript:jQuery如何将onclick事件绑定到动态添加的HTML元素...
  8. CAP 发布 5.0 版本正式发布
  9. oracle之基本的sql_select语句全
  10. spring boot 集成Mybatis时 Invalid bound statement (not found)
  11. 基于Session的认证方式_实现授权功能_Spring Security OAuth2.0认证授权---springcloud工作笔记118
  12. [Java] 蓝桥杯ALGO-99 算法训练 薪水计算
  13. js日期时间控件------layDate
  14. 远程连接桌面报:这可能是由于credssp加密oracle修正
  15. 你所不知道的我国交通工程早期经历了怎样的发展?
  16. 矩阵转置,矩阵加,矩阵乘Java
  17. 金融数据分析(十)人均国内生产总值的增长率
  18. 已解决The method is not allowed for the requested URL.
  19. Kali忘记root密码强制更改办法
  20. 微信公众号鸿蒙盾额度认证为什么要交钱,微信公众号收费标准是什么?认证流程介绍!...

热门文章

  1. JavaScript OnClick事件以及事件绑定方法
  2. Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释
  3. xposed修改手机信息
  4. 如何提升直通车人群标签精准度?
  5. iPhone不再孤独,Palm Pre为伴——互联网的未来在手中。,互联网营销
  6. python读取文件并删掉部分内容
  7. GridView添加横向滚动条
  8. Linux如何查看系统信息
  9. 解决更换微信公众号后提现签名错误的问题
  10. android+sd卡数据恢复,Android备份和恢复SD卡的数据库