youyichannel

志于道,据于德,依于仁,游于艺!

0%

线程池 02

线程池的参数

1)corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。

2)runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。建议选用有界队列,原因如下:

  1. 如果是无界队列,那么阻塞队列永远不会满,不会触发到maximumPoolsize,意味着maximumPoolsize这个参数没有作用
  2. 最重要的是无界队列无法控制队列最终包含的数据量,导致内存资源的极大的消耗甚至耗尽
  3. 选用有界队列并合理的配置maximumPoolsize
  4. 饱和策略的使用根据需求选择。一旦触发了饱和策略,就说明要么是线程池配置存在问题,要么是并发量太高,任务太多导致的问题。及时提醒去做调整

3)maximumPoolsize(线程池最大数量):线程池允许创建的最大线程数。

4)ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的 线程设置更有意义的名字。

5)RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。在 JDK 1.5 中 Java 线程池框架提供了以下 4 种策略。

  • AbortPolicy:直接抛出异常。
  • CallerRunsPolicy:只用调用者所在线程来运行任务。
  • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  • DiscardPolicy:不处理,丢弃掉。

也可以根据应用场景需要来实现 RejectedExecutionHandler接口自定义策略(最推荐), 如记录日志或持久化存储不能处理的任务。

6)keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。

7)TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。

合理的配置线程池

要想合理地配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析。

  • 任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
  • 任务的优先级:高、中和低。
  • 任务的执行时间:长、中和短。
  • 任务的依赖性:是否依赖其他系统资源,如数据库连接。

性质不同的任务可以用不同规模的线程池分开处理。CPU 密集型任务应配置尽可能小的线程,如配置 Ncpu+1 个线程的线程池。由于 IO 密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如 2*Ncpu。混合型的任务,如果可以拆分,将其拆分成一个 CPU 密集型任务和一个 IO 密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。

核心

  1. 明确业务环境,接口的重要性
  2. 线程池的配置需要基于压测,来评估线程池的参数设置是否合理
  3. 需要动态配置线程池参数,可以结合配置中心