龙空技术网

我面试别人问线程池,很少全答对。不难,4篇短文讲透(1)

程序员Artist 1138

前言:

此刻朋友们对“java线程资源”大体比较珍视,同学们都需要知道一些“java线程资源”的相关资讯。那么小编同时在网络上汇集了一些关于“java线程资源””的相关资讯,希望各位老铁们能喜欢,朋友们快快来了解一下吧!

大家好,我是十年程序员架构师小A。

序言

Java Jdk中的线程池问题,小A出去面试被问过四五次(前些年理解也不透彻)。而我面试别人也问过几十次,完全答对的几乎没有,多数候选人只能答对一半。

但,其实线程池以及各种其他的池化的技术都不难,原理相同、实现也很接近。一篇长文就能全部讲明白,但是为了保证技术文章足够简洁明了,不至于表面看着很长很难、半途放弃,小A准备拆分成四篇短文来彻底讲明白。

不仅讲明白线程池,也希望讲明白池化技术本身。

这四篇分别是:

由来,线程池有什么用?线程数设置多大合适(没那么简单,也没那么复杂)?计算,IO、CPU密集型场景,分别指什么,有哪些例子?思考,线程池最小最大线程、队列数设置及监控验证,流转方式?为什么这么设计?类比,还有哪些池化技术,什么时候需要池化?tomcat连接池、数据库和RPC连接池。

(不要担心,每一篇都很短、很简单)

所有的编程技术本质都极其简单,只是大多数人看的是三手资料,自己就没理解透,他们写的总结学习文章是四手资料,更难理解。而我们很难找到一手资料,深入浅出很难,大多数文章来自浅入深出。

本文是讲透线程池第一篇:

第1篇,由来

线程池有什么用?

为了复用需要频繁使用到的“重”资源,以达到降低损耗进而提高性能的目的。

前半句,重点是两个词,频繁使用、“重”资源。

频繁使用:

针对后端程序来说,输入的源头都是“用户”请求,一般情况下,一个用户请求需要独立占用一个后端处理线程,而用户请求是7*24小时不间断的,假如每秒有10个用户请求,每小时需要使用的线程次数是:10*24*60*60=86万4千次。

“重”资源:

笼统来说,程序执行系统调用,需要操作系统来协助处理的时候,耗费的资源就是重资源。比如,分配一段内存、CPU分片执行计算、本机磁盘读写(磁盘IO)、网卡读写远程资源(网络IO)。

回到线程来看,根据Xss参数设置,假设是1MB。则按上述举例,假设不使用线程池,每次都是创建一个新的线程来应对(除了耗时会稍高,这么做也可以“勉强正常”运行),则每小时需要创建86万多个线程,需要的内存资源累计是860GB。

如果采用线程池,设置5个线程来复用,则所需内存资源累计是5M,资源消耗降低了近20万倍。这里仅仅说的是内存资源的角度。其实,不使用线程池,频繁创建线程也会导致线程数较多,引起cpu的来回切换,也会耗费更多的CPU资源。

复用:

用户请求来了,从线程池里“借”一个空闲线程,执行逻辑完了后,再还回池子里。然后,不断重复上述借还的动作,就是复用。(如果线程池里的线程被借完了,还有请求来了怎么办?答案是:通常情况下,进等待队列里排队,等别人先还)

后半句,重点是两个词:降低损耗、提高性能。

复用,可以降低重资源使用成本。

提高性能,一方面得益于复用,可以避免创建线程等待;另一方面,线程池还可以用于“大拆小”场景,将一个大任务拆分为n个小任务,每个小任务分配一个线程并发执行,则任务的执行时间,通常会降低到n分之一。

线程数设置多大合适(没那么简单,也没那么复杂)?

网上的文章描述,或者面试候选者的答案,通常会有如下几种:

使用的newCachedTheadPool,不作设置CPU核数左右IO密集型场景设置2倍核数,CPU密集1倍核数先随便设置,再测试观察(“怎么测试观察?”、“不清楚”)有一个复杂的公式可以计算

这些答案都不对,很多都是模棱两可的答案。主要原因,我认为有两点:1、网上或很简单,或很复杂的模棱两可的三手资料太多了,越看越不懂。2、调试线程池的高并发业务比较少,大多数人没有实操经验,纯靠二三手的理论知识很难掌握。

没那么简单:

使用newCachedTheadPool,看CPU核数这些简单的设置方法是不对的。因为没有区分计算场景,是IO还是CPU,也没有区分IO场景下不同的并发程度。

没那么复杂:

随便设置再观察测试,这个也是可以的,但是需要知道如何验证。

网上有一个复杂的计算公式,这个是比较准确的,但是有一个前提是,得是IO密集型的业务场景。

所以,设置多少:

首先,分析需要使用线程池的业务逻辑是IO密集型还是CPU密集型的。

如果是CPU密集型的业务场景:

根据需要计算资源的“多少”,分配少量资源,最大不超过CPU核数。网上很多说的是最大核心数加1,这个其实还好,只要是这个数量级下就可以。

在CPU密集下,线程数 = 最大不超过当前机器的CPU核数

(超过少量也没问题,保持一个数量级就行)

如果是IO密集型:

则可以不考虑CPU核数多少(什么意思呢?设置十倍、几十倍核数也没问题。因为绝大多数情况下在等待,不使用CPU)。主要根据两个因素来判断,并发度和每个请求处理耗时。

举例:

如果QPS=100,即每秒钟需要处理100个请求;每个请求平均处理耗时=50ms。

线程池需要设置多大才够用呢?

每个请求50ms,则一个线程1秒钟可以处理:1000ms/50ms = 20个请求。每秒钟需要处理100个请求,则需要线程数是:100个请求/20个请求 = 5个线程。

简单吧。如果非要得出一个公式出来,则简化公式如下:

在IO密集下,线程数 = QPS / (1000/单次请求耗时)

(与网上一个复杂的公式本质是一样的)

本文完。

本文针对线程数怎么设置,先讲结果,应付面试问设置数量足够了。后续三篇会继续分别解释:如何区分IO和CPU、线程池流转(运行方式)和验证、池化技术(也都非常简单)。

提示:

希望持续关注本系列及其他编程、职场、北漂相关文章的,也可以关注我的同名微信公众号:程序员Artist。

标签: #java线程资源