线程池参数应该如何设置
前提说明:
今天为啥要写这篇文章呢,主要是我面试的时候被问到了这样一个问题:
面试官: “你在工作中用到过池化技术吗?”
我:“用到过,用到过线程池”
面试官:“那你说一下,在实际的项目中你是如何设置线程池的各个参数的大小的?”
我:“如果是CPU密集型任务就把线程数配置为N+1,核数+1;如果是IO密集型任务就配置为2N”
面试官:“你真的在工作中实际配置过这些参数吗?”
我:“en…,别人写好了,我直接用的” 内心:tmd我刚工作,这东西怎么会让我来配…
相信很多小伙伴们和我一样。面试前看八股文的时候都看到过“如果是CPU密集型任务就把线程数配置为N+1,核数+1;如果是IO密集型任务就配置为2N” 这样的描述。所以我们潜移默化的认为这个就是公式,别人问的时候答上来就行了。 但是有个很大的问题就是,如果你是应届生,八股是不会问这个的,反而你如果是社招很多都会问。如果在社招中我们也这么回答,可能就会回家等通知了。 那么我们应该如何回答呢? 等我下面来揭晓
首先需要先声明线程池的参数的配置没办法量化,不同的业务可能都会有不同的参数设置,只能理解一下里面的逻辑来方便我们在调试的时候明确思路,至少应该知道怎么调、是变大还是变小。
首先要明确线程池中最核心需要我们调整的就是下面几个参数
- corePoolSize:核心线程数
- maxPoolSize:最大线程数
- queueCapacity:队列大小
我们除了上面的三个外,还需要明确几个参数
- task:每秒执行的任务数
- taskTime:执行一个task所需要的时间
- responseTime:系统允许接受的最大响应时间
下面我们来一个一个说:
- 核心线程数
核心线程数和task以及taskTime有直接的关系,一般我们遵循一个公式:corePoolSize=task*taskTime
举个例子:如果task=1001000,taskTime=0.1s,那么我们的核心线程数的大小应该是task*taskTime=10100之间。而且我们一般遵循8020准则,就是task的数量以出现概率80%的为准。
理论上随着执行一个task所需要的时间的变长,我们的corePoolSize也会变大,为什么呢?因为:长时间运行的任务会占用核心线程,如果过多的长任务超过了核心线程数,可能导致其他任务排队等待执行,影响系统的性能。
注意:这里就不可以教条了,因为corePoolSize也不能太大,首先要考虑我们机器的配置,机器的cpu数量,此时我们在八股中学到的东西有用处了,为什么分为IO密集型和CPU密集型呢,如果我们仅仅教条的用task*taskTime这个公式的话,如果这是一个CPU密集型的任务那么corePoolSize太多就会导致频繁的进行上下文切换,这个操作反而会带来更多的花销,反而降低系统的吞吐量和响应性能。
其中task以及taskTime的获取要通过压测来获取。
- 队列大小
队列大小的设置和核心线程数以及我们业务所允许的最大响应时间有关系。队列太大就会导致我们的最大响应时间的变大。我们一般遵循一个公式:
queueCapacity=(corePoolSize/taskTime)*responseTime
举个例子:taskTime=0.1s,corePoolSize=10,responseTime=2s,那么queueCapacity=(10/0.1)*2=200
注意:我们也不可以教条,在实际的应用中要看我们业务允许的最大响应时间
- 最大线程数大小
我认为最大线程数的大小要根据压测结果去确定了,没有啥公式
总结:
对于线程池参数的设置这样的面试题,我们把里面的逻辑说清楚,比如哪个参数和什么有关系,而不要一上来就说公式,要说清楚因果关系。另外,在工作中实际的配置参数的确定一定是通过压测尝试出来的,基本不会是计算算出来的。一定要注意。