java统计系统线程数_Java并发(八)计算线程池最佳线程数

目录

一、理论分析

二、实际应用

为了加快程序处理速度,我们会将问题分解成若干个并发执行的任务。并且创建线程池,将任务委派给线程池中的线程,以便使它们可以并发地执行。在高并发的情况下采用线程池,可以有效降低线程创建释放的时间花销及资源开销,如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及“过度切换”(在JVM中采用的处理机制为时间片轮转,减少了线程间的相互切换) 。

但是有一个很大的问题摆在我们面前,即我们希望尽可能多地创建任务,但由于资源所限我们又不能创建过多的线程。那么在高并发的情况下,我们怎么选择最优的线程数量呢?选择原则又是什么呢?

一、理论分析

关于如何计算并发线程数,有两种说法。

第一种,《Java Concurrency in Practice》即《java并发编程实践》8.2节 170页

对于计算密集型的任务,一个有Ncpu个处理器的系统通常通过使用一个Ncpu+ 1个线程的线程池来获得最优的利用率(计算密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作)。对于包含了 I/O和其他阻塞操作的任务,不是所有的线程都会在所有的时间被调度,因此你需要一个更大的池。为了正确地设置线程池的长度,你必须估算出任务花在等待的时间与用来计算的时间的比率;这个估算值不必十分精确,而且可以通过一些监控工具获得。你还可以选择另一种方法来调节线程池的大小,在一个基准负载下,使用 几种不同大小的线程池运行你的应用程序,并观察CPU利用率的水平。

给定下列定义:

Ncpu = CPU的数量

Ucpu = 目标CPU的使用率, 0 <= Ucpu <= 1

W/C = 等待时间与计算时间的比率

为保持处理器达到期望的使用率,最优的池的大小等于:

Nthreads = Ncpu x Ucpu x (1 + W/C)

你可以使用Runtime来获得CPU的数目:

int N_CPUS = Runtime.getRuntime().availableProcessors();

当然,CPU周期并不是唯一你可以使用线程池管理的资源。其他可以约束资源池大小的资源包括:内存、文件句柄、套接字句柄和数据库连接等。计算这些类型资源池的大小约束非常简单:首先累加出每一个任务需要的这些资源的总童,然后除以可用的总量。所 得的结果是池大小的上限。

当任务需要使用池化的资源时,比如数据库连接,那么线程池的长度和资源池的长度会相互影响。如果每一个任务都需要一个数据库连接,那么连接池的大小就限制了线程池的有效大小;类似地,当线程池中的任务是连接池的唯一消费者时,那么线程池的大小反而又会限制了连接池的有效大小。

如上,在《Java Concurrency in Practice》一书中,给出了估算线程池大小的公式:

Nthreads = Ncpu x Ucpu x (1 + W/C),其中

Ncpu = CPU核心数

Ucpu = CPU使用率,0~1

W/C = 等待时间与计算时间的比率

第二种,《Programming Concurrency on the JVM Mastering》即《Java 虚拟机并发编程》2.1节 12页

为了解决上述难题,我们希望至少可以创建处理器核心数那么多个线程。这就保证了有尽可能多地处理器核心可以投入到解决问题的工作中去。通过下面的代码,我们可以很容易地获取到系统可用的处理器核心数:

Runtime.getRuntime().availableProcessors();

所以,应用程序的最小线程数应该等于可用的处理器核数。如果所有的任务都是计算密集型的,则创建处理器可用核心数那么多个线程就可以了。在这种情况下,创建更多的线程对程序性能而言反而是不利的。因为当有多个仟务处于就绪状态时,处理器核心需要在线程间频繁进行上下文切换,而这种切换对程序性能损耗较大。但如果任务都是IO密集型的,那么我们就需要开更多的线程来提高性能。

当一个任务执行IO操作时,其线程将被阻塞,于是处理器可以立即进行上下文切换以便处理其他就绪线程。如果我们只有处理器可用核心数那么多个线程的话,则即使有待执行的任务也无法处理,因为我们已经拿不出更多的线程供处理器调度了。

如果任务有50%的时间处于阻塞状态,则程序所需线程数为处理器可用核心数的两倍。 如果任务被阻塞的时间少于50%,即这些任务是计算密集型的,则程序所需线程数将随之减少,但最少也不应低于处理器的核心数。如果任务被阻塞的时间大于执行时间,即该任务是IO密集型的,我们就需要创建比处理器核心数大几倍数量的线程。

我们可以计算出程序所需线程的总数,总结如下:

线程数 = CPU可用核心数/(1 - 阻塞系数),其中阻塞系数的取值在0和1之间。

计算密集型任务的阻塞系数为0,而IO密集型任务的阻塞系数则接近1。一个完全阻塞的任务是注定要挂掉的,所以我们无须担心阻塞系数会达到1。

为了更好地确定程序所需线程数,我们需要知道下面两个关键参数:

处理器可用核心数;

任务的阻塞系数;

第一个参数很容易确定,我们甚至可以用之前的方法在运行时查到这个值。但确定阻塞系数就稍微困难一些。我们可以先试着猜测,抑或采用一些性能分析工具或java.lang.management API来确定线程花在系统IO操作上的时间与CPU密集任务所耗时间的比值。

如上,在《Programming Concurrency on the JVM Mastering》一书中,给出了估算线程池大小的公式:

线程数 = Ncpu/(1 - 阻塞系数)

对于说法一,假设CPU 100%运转,即撇开CPU使用率这个因素,线程数 = Ncpu x (1 + W/C)。

现在假设将方法二的公式等于方法一公式,即Ncpu /(1 - 阻塞系数)= Ncpu x (1 + W/C),推导出:阻塞系数 = W / (W + C),即阻塞系数 = 阻塞时间 /(阻塞时间 + 计算时间),这个结论在方法二后续中得到印证,如下:

由于对Web服务的请求大部分时间都花在等待服务器响应上了,所以阻塞系数会相当高,因此程序需要开的线程数可能是处理器核心数的若干倍。假设阻塞系数是0.9,即每个任务90%的时间处于阻塞状态而只有10%的时间在干活,则在双核处理器上我们就需要开20个线程(使用第2.1节的公式计算)。如果有很多只股票要处理的话,我们可以在8核处理器上开到80个线程来处理该任务。

由此可见,说法一和说法二其实是一个公式。

二、实际应用

那么实际使用中并发线程数如何设置呢?我们先看一道题目:

假设要求一个系统的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吞吐量加进去。

分析如下(我们以说法一公式为例):

Nthreads = Ncpu x (1 + W/C)

即线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。这就可以划分成两种任务类型:

IO密集型:一般情况下,如果存在IO,那么肯定W/C > 1(阻塞耗时一般都是计算耗时的很多倍),但是需要考虑系统内存有限(每开启一个线程都需要内存空间),这里需要在服务器上测试具体多少个线程数适合(CPU占比、线程数、总耗时、内存消耗)。如果不想去测试,保守点取1即可,Nthreads = Ncpu x (1 + 1) = 2Ncpu。这样设置一般都OK。

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

根据短板效应,真实的系统吞吐量并不能单纯根据CPU来计算。那要提高系统吞吐量,就需要从“系统短板”(比如网络延迟、IO)着手:

尽量提高短板操作的并行化比率,比如多线程下载技术;

增强短板能力,比如用NIO替代IO;

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

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

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

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

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

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

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

多线程带来线程上下文切换开销,单线程就没有这种开销;

锁;

当然“Redis很快”更本质的原因在于:

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

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

至此结论为:

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

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

当然说法一中还有一种说法:

对于计算密集型的任务,一个有Ncpu个处理器的系统通常通过使用一个Ncpu+ 1个线程的线程池来获得最优的利用率(计算密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作)。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457237.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php大小写转换函数

1.将字符串转换成小写 strtolower(): 该函数将传入的字符串参数所有的字符都转换成小写,并以小定形式放回这个字 符串.例: <?php$str "I want To FLY";$str strtolower($str);echo $str; ?>输出结果: i want to fly 2.将字符转成大写 strtoupper(): 该…

关于移动端 1px 像素问题

移动端1px变粗的原因 移动端html的header总会有一句<meta name"viewport" content"widthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno">这句话定义了本页面的viewport的宽度为设备宽度,初始缩放值和最大缩放值都为1,并禁止了…

java框架概念_java概念(2)

java概念(2)重载和重写重载&#xff1a;同一个类中&#xff0c;方法名相同&#xff0c;参数不同重写&#xff1a;父子类中&#xff0c;子类重新定义父类的方法多态​ 多态&#xff1a;同一种行为&#xff0c;不同的对象有不同的表现形式。​ 重载 编译时根据参数决定调用的方法…

CentOS(八)--crontab命令的使用方法

crontab命令常见于Unix和Linux的操作系统之中&#xff0c;用于设置周期性被执行的指令。该命令从标准输入设备读取指令&#xff0c;并将其存放于"crontab"文件中&#xff0c;以供之后读取和执行。 在Linux系统中&#xff0c;Linux任务调度的工作主要分为以下两类&…

有健忘症吗?

今天兴高采烈&#xff0c;早上空气不错&#xff0c; 但是骑自行车的我&#xff0c;还是得戴一个面罩。 半个小时后买了早餐&#xff0c; 一份炒粉、一豆浆&#xff0c;今天早上豆浆没有掉地上&#xff0c; 但是~~~~~~~~~~~~~~shit~~!~!~,居然忘记带要换的衣服了&#xff0c; …

下载java后缀的文件闪退_关于jarfile 打开闪退问题

后面才发现&#xff0c;原来是因为我把文件拖入了新建的文件夹&#xff0c;改变了路径&#xff0c;而且我的java环境没有配置好是全局变量&#xff0c;所以新建文件夹之后&#xff0c;就会出现找不到了路径&#xff0c;闪退的问题&#xff0c;&#xff0c;&#xff0c;还有就是…

心理学资源整理

http://blog.sina.com.cn/s/articlelist_1227187337_0_1.html 功夫 转载2016-06-07 15:53:55中华大地的武学&#xff0c;奥妙精深。但从大的方面来说&#xff0c;可分为内外软硬&#xff0c;有形无形的区别。注重于外&#xff0c;修炼筋骨皮肉的是有形的功夫&#xff1b;注重于…

[已解决]Vistual Stdio 2015 installer Bootstrapper Packages 路径

VS2015 installer 的预装包的地址变更成 C:\Program Files (x86)\Microsoft Visual Studio 14.0\SDK\Bootstrapper\Packages 参看文档&#xff1a;https://msdn.microsoft.com/en-us/library/ms165429(vvs.140).aspx 之前的版本是在 C:\Program Files (x86)\Microsoft SDKs\Win…

java怎样写入五个人的成绩_用java输入5个学员姓名和分数,显示分数最高的学员姓名和分数?...

展开全部import java.util.Scanner;public class Student {private String stuname "";private float stuscore 0;public String getStuname() {e69da5e6ba9062616964757a686964616f31333335316633return stuname;}public void setStuname(String stuname) {this.s…

eBay宣布发布全新的购买和销售APIs

eBay最近宣布发布两款全新的购买和销售APIs。这些APIs旨在促进eBay产品在第三方应用程序中的更好集成。eBay于10月19日在他们的博客上发表了几篇文章&#xff0c;不仅详细介绍了这些全新的购买和销售APIs提供的功能&#xff0c;而且还详细地总结了他们公司从SOAP&#xff08;简…

iOS 10 升级后无法真机测试 Could not find Developer Disk Image

&#xff0d;&#xff0d;&#xff0d;2016年9月20日更新 iOS 升级到10之后&#xff0c;你会发现无法进行真机测试了。这种情况我在iOS 8.4 、9.3更新的时候也遇到过。原因是Xcode 的DeviceSupport里面缺少了iOS 10的SDK。所以你可以选择将Xcode更新到最新版本就可以了&#xf…

java虚拟机参数优化_JAVA虚拟机JVM参数优化(2):垃圾收集算法选择

JAVA虚拟机JVM优化重要性&#xff0c;昨天JAVA虚拟机JVM参数优化(1)文章中已经描述&#xff0c;今天我们来讨论JAVA虚拟机在不同性能要求下如何选择三种垃圾收集算法。JVM内部结构如下图所示&#xff1a;串行收集用于单个线程执行垃圾收集的情况&#xff0c;在这种情况下相对它…

Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)

2019独角兽企业重金招聘Python工程师标准>>> 互联网的发展&#xff0c;网站应用的规模不断扩大&#xff0c;常规的垂直应用架构已无法应对&#xff0c;分布式服务架构以及流动计算架构势在必行&#xff0c;Dubbo是一个分布式服务框架&#xff0c;在这种情况下诞生的…

java clicked_关于java:JComponents在调用mouseClicked()之后消失

我正在用Swing编写Java GUI程序。该界面如下所示&#xff1a;当用户单击右侧的图片之一时&#xff0c;我希望它的一个小的预览显示在左上角的橙色区域中。我通过SwingWorker线程从计算机上的目录中提取所有图像文件。在SwingWorker的done()方法中&#xff0c;我向每个对象添加了…

vim简单命令教程-firstblood

你想以最快的速度学习人类史上最好的文本编辑器VIM吗&#xff1f;你先得懂得如何在VIM幸存下来&#xff0c;然后一点一点地学习各种戏法。 Vim the Six Billion Dollar editor Better, Stronger, Faster. 学习 vim 并且其会成为你最后一个使用的文本编辑器。没有比这个更好的文…

第三课、Qt的诞生和本质------------------狄泰软件学院

一、GUI用户界面元素 &#xff08;1&#xff09;、GUI应用程序是由固定的窗口元素所构成 &#xff08;2&#xff09;、操作系统提供了创建用户界面元素所需要的函数 &#xff08;3&#xff09;、各自功能不同的函数依次调用&#xff0c;从而创建出界面元素 &#xff08;4&#…

java mapstring_ object 遍历_ListMapString,Object使用Java代码遍历以获取String,Object的值...

List>的结果集怎么使用Java代码遍历以获取String&#xff0c;Object的值&#xff1f;package excel;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class List1{public static void main(String[] args){Map map…

Linux tar命令高级用法——备份数据

Linux tar命令高级用法——备份数据 2015-12-31 Linux学习Linux上有功能强大的tar命令&#xff0c;tar最初是为了制作磁带备份&#xff08;tape archive&#xff09;而设计的&#xff0c;它的作用是把文件和目录备份到磁带中&#xff0c;然后从磁带中提取或恢复文件。现在我们可…

iOS uiviewcontroller 添加另外一个controller的View

需要 添加 [self addChildViewController:vc]; [_mainScrollView addSubview:vc.view];转载于:https://www.cnblogs.com/foolish-guo/p/6385288.html

mysql 即学a又学b_MySQL学习第一天

一、 数据库:*学习重点:创建数据库/向表添加记录/查询记录数据库概念:文件系统(存储和管理)数据库软件介绍:Oracle/MySQL/SQL server…分类:关系型数据库:关系模型组织数据非关系型数据库:键值对关系存储二、 mysql数据的存储方式:一台数据库服务器中会创建很多个数据库(一个项…