线程池如何更好的使用

前言

  对于如何更合适的使用线程池,根据系统业务是CPU密集型 还是IO密集型。

cpu密集型:
    cpu使用率较高(也就是一些复杂运算,逻辑处理),所以线程数一般只需要cpu核数的线程就可以了。 这一类型的在开发中多出现的一些业务复杂计算和逻辑处理过程中。

I/O密集型:
       cpu使用率较低,程序中会存在大量I/O操作占据时间,导致线程空余时间出来,所以通常就需要开cpu核数的两倍的线程, 当线程进行I/O操作cpu空暇时启用其他线程继续使用cpu,提高cpu使用率 通过上述可以总结出:

  线程的最佳数量: 最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

  线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。这一类型在开发中主要出现在一些读写操作频繁的业务逻辑中。

  --------------------------------------------------------------------------------------------------------------

       开发中我们经常会使用到线程池来处理一些业务,而在不新增设备的情况下,我们所能使用的线程资源又不是无线的,那么高并发、任务执行时间短的业务怎样使用线程池?还有并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?

接下来我们进行一一分析:

1:高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换

2:并发不高、任务执行时间长的业务这就需要区分开看了:

a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目,让CPU处理更多的业务

b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换

(其实从一二可以看出无论并发高不高,对于业务中是否是cpu密集还是I/O密集的判断都是需要的当前前提是你需要优化性能的前提下)

3:并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,我们的项目使用的时redis作为缓存(这类非关系型数据库还是挺好的)。增加服务器是第二步(一般政府项目的首先,因为不用对项目技术做大改动,求一个稳,但前提是资金充足),至于线程池的设置,设置参考 2 。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件(任务时间过长的可以考虑拆分逻辑放入队列等操作)对任务进行拆分和解耦。

   --------------------------------------------------------------------------------------------------------------

ThreadPoolExecutor参数设计

   我们知道通过Executors的静态方法创建不同种类线程池,但实质都是传递不同参数给ThreadPoolExecutor的构造方法。corePooleSize、maxPoolSize、BlockingQueue之间的关系和线程池创建的过程就不说了,下面简单说他们的关系,详情看个人的其他文章。

  • corePoolSize是核心线程数,maxPoolSize是最大线程数,BlockingQueue是任务队列
  • 当有任务提交时,先创建corePoolSize数量的线程,有更多的任务则进入到BlockingQueue,BlockingQueue满了还不够则创建线程数直到macPoolSize
  • 线程空闲一段时间后会被销毁直到线程池中只剩下core数量的线程

如何设计这些参数值呢?

通过例子来说明:

1. 假如提交到线程池中的任务,IO耗时占比是80%,计算耗时占比20%,忽略提交到线程池中的任务数量,在4C8G的机器上,理想情况下线程池中创建多少个线程是最优的?

  线程数=(1/0.2) * 4 = 20

2. 假如e有一类cpu密集性的任务,没有IO操作,日常的时候只有1个任务,流量高峰会有50个任务,4C8G的机器上,使用的线程池,如何设置corePoolSize, maxPoolSize以及BlockingQueue的大小?

 回答1:core=4, max=50, queue=1w。maxPoolSize数量的线程永远不会被创建,队列设置成1w,这个队列太大,根本就不会满。这样是不理想的 造成资源的浪费。

  日常每秒1个任务时,只需要一个线程就够了,创建出4个线程,就有3个浪费。

 回答2:

  很显然,日常只需要一个线程,那么corePoolSize=1,而高峰时候,虽然任务有50个,但是只是4C的机器,对于cpu密集型任务,4个线程是最优的,因此理想情况下maxCorePoolSize=4,最后再看看队列,因为队列满了,max才会被创建,而我们需要让max快速被创建出来,又不会出现任务拒绝,因此,可将队列大小设置成46,那么线程池的行为如下:

  • 提交第一个任务,创建出core,1个线程
  • 提交第二个到第47个任务时,这些任务进入到队列中,此时队列已满
  • 提交第48个任务到第50个任务时,创建出max,此时一共有4个线程
  • 4个线程同时将队列里的46个任务消费完
  • 一段时间后,max - core数量的线程销毁,即销毁3个线程,还剩下一个线程

参考:https://zhuanlan.zhihu.com/p/86433815

原文地址:https://www.cnblogs.com/FondWang/p/12457450.html