前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >创建多少线程是合适的

创建多少线程是合适的

作者头像
小土豆Yuki
发布2020-12-16 09:48:10
7320
发布2020-12-16 09:48:10
举报
文章被收录于专栏:洁癖是一只狗

面试中经常有人被问到线程池的数据设置多少合适呢,今天我们就看一下这个问题,首先我们需要知道两个问题

  1. 为什么使用多线程
  2. 多线程使用的场景

为什么使用多线程

使用多线程是主要目的就是提高性能,而性能的指标有很多,最主要的性能指标就是延迟和吞吐量

延迟是指发出请求到接受到请求这个过程的时间

吞吐量是指单位时间内处理的请求的数量.

延迟越低,吞吐量就越高,由于他们隶属于不同维度,一个是时间维度,一个是空间维度,因此不能相互转换

我们使用多线程的目的就是降低延迟提高吞吐量

多线程使用的场景

提升性能就要是两个方向,一个就是优化算法,一个就是提高硬件的性能能发挥到机制,我使用多线程就是要把硬件的性能提高到机制,而硬件的主要分两类一类就是io,一个就是cpu,在并发编程领域中,提升性能就是提高硬件的利用率,在具体点来说就是提升io和cpu的利用率

正常来说,操作系统已经解决了硬件的利用问题,但是操作系统是对单一的硬件设备,比如网卡和磁盘,而我们往往是要利用io和cpu互相配合使用,操作系统提供给我们的方案就是多线程

举个例子,此时的cpu计算和io操作的耗时就是1:1,而我们只有一个线程,在执行CPU计算的时候io设备闲置,而使用io操作的时候cpu闲置,如下图

但是如果我们使用两个线程,当线程a使用cpu的时候,线程b使用io,而当线程a使用io的使用线程b使用cpu,这个时候我们的cpu和io就可以达到100%。如下图

此时我们很容易发现如果cpu和io利用率很低的时候,是不是就可以增加线程来解决呢,在单核时代我们可以用多线程平衡cpu和io设备,如果程序只有cpu计算,而没有io操作,此时的多线程只会带来切换的成本,只会使性能更差,但是在多核时代,我们就可以使用多线程来提高性能,利用多核减低响应时间

比如我们要计算1+2....+100亿的值,此时我们使用的是四核,我们就使用4个线程分别计算四个区段,[1,25亿)[25亿,50亿),[50亿,75亿),[75亿,100亿],最后汇总他的结果,如下图

创建多少个线程合适呢

创建线程的多少,是要区分场景的,一般是cpu密集性和io密集性,这两种场景的是不同的计算方式

对于CPU密集型计算,多线程本质上就是提升多核cpu的利用率,所以一个4核的cpu,每一个核创建一个线程,理论上创建4个线程就可以了,再多的线程只会增加线程切换的成本,所以对于CPU密集型计算场景,理论上线程的数据数量=CPU核数是最合适的,但是一般都会设置cpu核数+1,那是因为当线程偶发内存也失效或其他原因导致阻塞,这个额外的线程就可以顶上,从而保证cpu的利用率

对于IO密集型计算场景,比如我们的CPU计算和I/O操作的耗时是1:2,那么创建3个线程是最好的,如下图,三个线程之间进行切换,使用cpu和io利用率达到最高100%

通过上面的例子我们发现,对于I/O密集型计算场景,最佳的线程是与程序中CPU计算和I/O计算操作的耗时比相关的,可以得出下面公式

代码语言:javascript
复制
最佳线程数 =1 +(I/O 耗时 / CPU 耗时)

我们令R=I/O耗时/CPU耗时,当线程A执行IO操作的时候,R个线程去执行各自的CPU计算,CPU的利用率就会达到100%

不过上面的公式是在单核下面,如果是多核cpu,可以使用下面公式

代码语言:javascript
复制
最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]

附加题

stop和interrupt()有什么区别

  1. stop是真的杀死线程,不给线程喘息的机会,如果线程持有ReentrantLock锁,被stop的线程并不会去调用ReentrantLock的unlock去释放锁,可能会导致死锁,不建议使用,类似的还有suspend和resume
  2. 而interrupt方法仅仅是通知线程,线程有机会执行一些后续的操作,同时也可以无视这个通知

那interrupt是如何通知的呢

一种是异常,一种是主动监测,当线程A处于waiting,timed_waiting状态时,如果其他线程调用线程A的interrupt,线程就会回到runable状态,同时线程A的代码会触发interruptException异常。

当线程A处于Runable状态,并且阻塞在java.nio.channels.InterruptibleChannel上时,如果其他线程调用线程A的interrupt方法,线程A就会触发java.nio.channels.ClosedByInterruptException异常,而阻塞在java.nio.channels.Selector上时候,如果其他线程调用线程Ainterrupt方法,线程A的java.nio.channels.Selector 会立即返回。

上面两种情况就是被中断且通过异常的方式得到了通知,还有一种就是主动检测,如果线程处于Runable状态,并且没有阻塞在某个I/O上,例如中断计算某个数据的线程A,这就得依赖线程A主动检测中断状态,如果其他线程A的interrupt方法,那么线程A可以通过isinterrupt方法,检测是不是自己被中断。

如果对您有一丝丝帮助,麻烦点个关注,也欢迎转发,谢谢

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档