如何合理设置线程池的核心线程数?

当线程池的核心线程数量过大或者过小有没影响?如何合理地设置线程池的核心线程的数量?这个是在日常开发中程序员在使用线程池时经常需要考虑的问题,下面具体介绍下。

1、当线程池的核心线程数量过大或者过小的影响

当线程池中核心线程数量过大时,线程与线程之间会争取CPU资源,这样就会导致上下文切换。过多的上下文切换会增加线程的执行时间,影响了整体执行的效率;

多线程编程中一般线程的个数都大于CPU核心的个数,而一个CPU核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效的执行,CPU采取的策略是为了每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让其他线程使用,这个过程就属于一次上下文切换。

当线程池中的核心线程数量过少时,如果同一时间有大量任务需要处理,可能会导致大量任务在任务队列中排队等待执行,甚至会出现队列满了之后任务无法执行的情况,或者大量任务堆积在任务队列导致内存溢出(OOM)。

2、任务性质

在讨论设置核心线程数之前,先来了解下任务的性质,主要分为CPU密集型(计算密集型)任务、I/O密集型任务、混合型任务:

CPU密集型(计算密集型):系统的I/O读写效率高于CPU效率,大部分的情况是CPU有许多运算需要处理,使用率很高。但I/O执行很快。

I/O密集型:系统的CPU性能比磁盘读写效能要高很多,大多数情况是CPU在等I/O的读写操作,此时CPU的使用率并不高;

混合型任务:既包含CPU密集型又包含I/O密集型。

3、合理设置核心线程数

对于CPU密集型任务,由于CPU密集型任务的性质,导致CPU的使用率很高,如果线程池中的核心线程数量过多,会增加上下文切换的次数,带来额外的开销。因此,一般情况下线程池的核心线程数量等于CPU核心数+1。(注:这里核心线程数不是等于CPU核心数,是因为考虑CPU密集型任务由于某些原因而暂停,此时有额外的线程能确保CPU这个时刻不会浪费。但同时也会增加一个CPU上下文切换,因此核心线程数是等于CPU核心数?还是CPU核心数+1?可以根据实际情况来确定)

对于I/O密集型任务,由于I/O密集型任务CPU使用率并不是很高,可以让CPU在等待I/O操作的时去处理别的任务,充分利用CPU。因此,一般情况下线程的核心线程数等于2*CPU核心数。(注:有些公司会考虑所需要的CPU阻塞系数,即核心线程数=CPU核心数/(1-阻塞系数))

对于混合型任务,由于包含2种类型的任务,故混合型任务的线程数与线程时间有关。一般情况下:线程池的核心线程数=(线程等待时间/线程CPU时间+1)*CPU核心数;在某种特定的情况下还可以将任务分为I/O密集型任务和CPU密集型任务,分别让不同的线程池去处理,但有一个前提–分开后2种任务的执行时间相差不太大。

Java线程池如何合理配置核心线程数 线程池核心线程数设置_java

总结:

CPU密集型:核心线程数=CPU核心数(或 核心线程数=CPU核心数+1)

I/O密集型:核心线程数=2*CPU核心数(或 核心线程数=CPU核心数/(1-阻塞系数))

混合型:核心线程数=(线程等待时间/线程CPU时间+1)*CPU核心数