重复管理线程,避免创建大量的线程增加开销

在使用线程池的时候,会有两个疑问点:

  • 线程池的线程数量设置过多会导致线程竞争激烈
  • 如果线程数量设置过少的话,还会导致系统无法充分利用计算机资源

    线程池框架 Executor

    Executors 实现了以下四种类型的 ThreadPoolExecutor:
    tauke3mh8a.png
    Executors 利用工厂模式实现的四种线程池,我们在使用的时候需要结合生产环境下的实际场景。因为选择使用 Executors 提供的工厂类,将会忽略很多线程池的参数设置,工厂类一旦选择设置默认参数,就很容易导致无法调优参数设置,从而产生性能问题或者资源浪费。
    建议使用 ThreadPoolExecutor 自我定制一套线程池(阿里规范中也是建议不要使用Executors 创建线程池,建议使用ThreadPoolExecutor 来创建线程池)。
    image.png
    corePoolSize:线程池的核心线程数量
    maximumPoolSize:线程池的最大线程数
    keepAliveTime:当线程数大于核心线程数时,多余的空闲线程存活的最长时间
    unit:时间单位
    workQueue:任务队列,用来储存等待执行任务的队列
    threadFactory:线程工厂,用来创建线程,一般默认即可
    handler:拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务image.png
    图解多线程之间的关系:
    eh0ft3fagg.png
    由此发现,在创建完线程池之后,线程池中没有任何线程,等到有任务来才创建线程去执行任务。
    当创建的线程数等于 corePoolSize 时,提交的任务会被加入到设置的阻塞队列中。当队列满了,会创建线程执行任务,直到线程池中的数量等于 maximumPoolSize。
    当线程数量已经等于 maximumPoolSize 时, 新提交的任务无法加入到等待队列,也无法创建非核心线程直接执行,我们又没有为线程池设置拒绝策略(源码中默认为拒绝策略)

  • AbortPolicy 丢弃任务,抛运行时异常。

  • CallerRunsPolicy 执行任务。
  • DiscardPolicy 忽视,什么都不会发生。
  • DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务。

image.png,这时线程池就会抛出 RejectedExecutionException 异常,即线程池拒绝接受这个任务。
当线程池中创建的线程数量超过设置的 corePoolSize,在某些线程处理完任务后,如果等待 keepAliveTime 时间后仍然没有新的任务分配给它,那么这个线程将会被回收。线程池回收线程时,会对所谓的“核心线程”和“非核心线程”一视同仁,直到线程池中线程的数量等于设置的 corePoolSize 参数,回收过程才会停止。
即使是 corePoolSize 线程,在一些非核心业务的线程池中,如果长时间地占用线程数量,也可能会影响到核心业务的线程池,这个时候就需要把没有分配任务的线程回收掉。
我们可以通过 allowCoreThreadTimeOut 设置项要求线程池:将包括“核心线程”在内的,没有任务分配的所有线程,在等待 keepAliveTime 时间后全部回收掉。
ydb5rw9m8o.png
总结:
配置最大线程数maximumPoolSize,需要看我们的业务是IO密集型还是CPU密集型
CPU密集型:
一般公式:(CPU核数+1)个 线程的线程池
IO密集型
CPU核数 2
CPU核数 / (1 - 阻塞系数) 阻塞系数 在0.8~0.9之间
*查看CPU核数

  1. System.out.println(Runtime.getRuntime().availableProcessors());