当前位置: 首页 > news >正文

并发下线程池的最佳数量计算

在高并发的情况下采用线程池,有效的降低了线程创建释放的时间花销及资源开销,如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。(在JVM中采用的处理机制为时间片轮转,减少了线程间的相互切换)
那么在高并发的情况下,我们怎么选择最优的线程数量呢?选择原则又是什么呢?这个问题去哪网的技术总监问过我,这里总结一下。
第一种:

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

如果是IO密集型应用,则线程池大小设置为2N+1
  • 1
  • 2

任务一般可分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。CPU密集型任务 尽量使用较小的线程池,一般为CPU核心数+1。 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。IO密集型任务 可以使用稍大的线程池,一般为2*CPU核心数。 IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。混合型任务 可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。 只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。 因为如果划分之后两个任务执行时间相差甚远,那么先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失。

第二种呢,先由之前遇到的一个测试题说起,假设要求一个系统的TPS(Transaction Per Second或者Task Per Second)至少为20,然后假设每个Transaction由一个线程完成,继续假设平均每个线程处理一个Transaction的时间为4s。那么问题转化为:

如何设计线程池大小,使得可以在1s内处理完20个Transaction?

计算过程很简单,每个线程的处理能力为0.25TPS,那么要达到20TPS,显然需要20/0.25=80个线程。
这个理论上成立的,但是实际情况中,一个系统最快的部分是CPU,所以决定一个系统吞吐量上限的是CPU。增强CPU处理能力,可以提高系统吞吐量上限。在考虑时需要把CPU吞吐量加进去。在IO优化文档中,有这样地公式:
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
即线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。
但根据短板效应,真实的系统吞吐量并不能单纯根据CPU来计算。那要提高系统吞吐量,就需要从“系统短板”(比如网络延迟、IO)着手:

尽量提高短板操作的并行化比率,比如多线程下载技术
增强短板能力,比如用NIO替代IO


尽量提高短板操作的并行化比率,比如多线程下载技术
增强短板能力,比如用NIO替代IO
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第一条可以联系到Amdahl定律,这条定律定义了串行系统并行化后的加速比计算公式:

加速比=优化前系统耗时 / 优化后系统耗时
  • 1
  • 2

加速比越大,表明系统并行化的优化效果越好。Addahl定律还给出了系统并行度、CPU数目和加速比的关系,加速比为Speedup,系统串行化比率(指串行执行代码所占比率)为F,CPU数目为N:

Speedup <= 1 / (F + (1-F)/N)
  • 1
  • 2

当N足够大时,串行化比率F越小,加速比Speedup越大。

这时候又抛出是否线程池一定比但线程高效的问题?

答案是否定的,比如Redis就是单线程的,但它却非常高效,基本操作都能达到十万量级/s。从线程这个角度来看,部分原因在于:

多线程带来线程上下文切换开销,单线程就没有这种开销
锁
  • 1
  • 2
  • 3

当然“Redis很快”更本质的原因在于:
Redis基本都是内存操作,这种情况下单线程可以很高效地利用CPU。而多线程适用场景一般是:存在相当比例的IO和网络操作。

总的来说,应用情况不同,采取多线程/单线程策略不同;线程池情况下,不同的估算,目的和出发点是一致的。

参考借鉴:http://ifeve.com/how-to-calculate-threadpool-size/
https://www.zhihu.com/question/38128980

相关文章:

  • SpringMVC+MYBatis企业应用实战笔记
  • HTML5本地存储使用详解
  • 95后博士入职达摩院,14岁上大学,成阿里史上最年轻科学家
  • [Node.js]连接mongodb
  • 数据分析——四种大数据分析方法
  • Maven POM详解
  • 技术指导实践指南
  • @EnableAsync和@Async开始异步任务支持
  • Jenkins+Kubernetes CI/CD
  • YARN中内存的设置
  • 责任链模式 以及在Android开发中的运用
  • 学习打卡-2018/08/07
  • Micro Service Architecture — Timeout
  • 通用清除浮动方式
  • Struts2之校验
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • C# 免费离线人脸识别 2.0 Demo
  • HashMap ConcurrentHashMap
  • iOS编译提示和导航提示
  • javascript从右向左截取指定位数字符的3种方法
  • JavaScript服务器推送技术之 WebSocket
  • jdbc就是这么简单
  • Koa2 之文件上传下载
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • 多线程 start 和 run 方法到底有什么区别?
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 你不可错过的前端面试题(一)
  • 前端面试题总结
  • 深度学习在携程攻略社区的应用
  • 使用 QuickBI 搭建酷炫可视化分析
  • 在Mac OS X上安装 Ruby运行环境
  • ​​​​​​​​​​​​​​Γ函数
  • ​你们这样子,耽误我的工作进度怎么办?
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • #pragma预处理命令
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (a /b)*c的值
  • (C语言)共用体union的用法举例
  • (八)Spring源码解析:Spring MVC
  • (二)c52学习之旅-简单了解单片机
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (四)JPA - JQPL 实现增删改查
  • (转)Linux下编译安装log4cxx
  • (转)nsfocus-绿盟科技笔试题目
  • .net 设置默认首页
  • @column注解_MyBatis注解开发 -MyBatis(15)
  • @Service注解让spring找到你的Service bean
  • @Transactional类内部访问失效原因详解
  • [BZOJ 4598][Sdoi2016]模式字符串
  • [C# 开发技巧]实现属于自己的截图工具
  • [C#] 我的log4net使用手册