线程池 ThreadPoolExecutor 核心笔记:7 参数构造方法与拒绝策略

一、核心构造方法(7 参数)

ThreadPoolExecutor 是 Java 线程池的核心实现类,其 7 参数构造方法定义了线程池的核心行为,具体参数如下:

1
2
3
4
5
6
7
8
9
public ThreadPoolExecutor(
int corePoolSize, // 1. 核心线程数
int maximumPoolSize, // 2. 最大线程数
long keepAliveTime, // 3. 线程空闲存活时间
TimeUnit unit, // 4. 时间计量单位
BlockingQueue<Runnable> workQueue, // 5. 任务队列
ThreadFactory threadFactory, // 6. 线程工厂
RejectedExecutionHandler handler // 7. 拒绝策略处理器
) {}

二、7 个参数详细解析

1. 核心线程数(corePoolSize)

  • 线程池的基础线程数量,线程池初始化时不会立即创建核心线程,而是在有任务提交时逐步创建。
  • 任务提交时的线程创建规则:
    • 若当前线程总数 < corePoolSize:新建核心线程执行任务;
    • 若当前线程总数 ≥ corePoolSize:新任务不会创建核心线程,会进入任务队列或创建非核心线程(需满足最大线程数限制)。
  • 核心线程的存活特性:默认情况下,核心线程会一直存活在线程池,即使处于空闲状态;若设置 allowCoreThreadTimeOut = true,核心线程空闲时间超过指定阈值后会被销毁。

2. 最大线程数(maximumPoolSize)

  • 线程池能容纳的最大线程总数,计算公式为:最大线程数 = 核心线程数 + 非核心线程数
  • 当任务队列满且核心线程全部繁忙时,线程池会创建非核心线程处理任务,但总线程数不能超过此值。

3. 线程空闲存活时间(keepAliveTime)

  • 用于控制空闲线程的销毁时机,默认仅作用于非核心线程:
    • 非核心线程空闲时长超过该值,会被线程池销毁以节省资源;
    • 若设置 allowCoreThreadTimeOut = true,该参数同样作用于核心线程,空闲核心线程超过时长也会被销毁。

4. 时间计量单位(TimeUnit)

  • 枚举类型,用于指定 keepAliveTime 的时间单位,常用取值如下:
    • TimeUnit.NANOSECONDS:纳秒(1 纳秒 = 1 微秒 / 1000)
    • TimeUnit.MICROSECONDS:微秒(1 微秒 = 1 毫秒 / 1000)
    • TimeUnit.MILLISECONDS:毫秒(1 毫秒 = 1 秒 / 1000)
    • TimeUnit.SECONDS:秒
    • TimeUnit.MINUTES:分
    • TimeUnit.HOURS:时
    • TimeUnit.DAYS:天

5. 任务队列(BlockingQueue<Runnable>)

  • 用于存储等待执行的任务,是一个单端阻塞队列(BlockingQueue 为接口,需使用具体实现类),常用实现类如下:
    • ArrayBlockingQueue:基于数组的有界阻塞队列,需指定固定容量,特点是线程安全、性能稳定,适合任务量可预估的场景。
    • LinkedBlockingQueue:基于链表的阻塞队列,默认容量为 Integer.MAX_VALUE(可视为无界队列),也可指定容量,适合任务量波动较大的场景,但需注意无界队列可能导致内存溢出。
    • SynchronousQueue:同步队列,无实际存储空间,提交的任务必须立即被线程执行,若无空闲线程则创建新线程(需配合最大线程数限制),适合任务执行时间短、并发量高的场景。
    • PriorityBlockingQueue:优先级阻塞队列,无界队列,任务按优先级排序执行,适合对任务执行顺序有优先级要求的场景。
    • DelayQueue:延迟队列,无界队列,任务需实现 Delayed 接口,仅在延迟时间到期后才会被执行,适合定时任务场景(如定时重试、定时通知)。

6. 线程工厂(ThreadFactory)

  • 用于定义线程的创建规则,可自定义线程名称、设置线程是否为守护线程(后台线程)、设置线程优先级等。
  • 日常开发中若无需特殊配置,可使用默认实现,无需额外自定义。

7. 拒绝策略处理器(RejectedExecutionHandler)

  • 当线程池达到最大线程数且任务队列已满时,新提交的任务会触发拒绝策略,用于定义任务的处理方式。

三、JDK 内置四种拒绝策略

1. AbortPolicy(终止策略)

  • 线程池默认拒绝策略。
  • 核心行为:直接拒绝新任务,抛出 RejectedExecutionException(运行时异常)。
  • 注意事项:若使用 execute() 方法提交任务且未通过 try-catch 捕获异常,会导致提交任务的线程终止(若为主线程则程序直接退出)。
  • 适用场景:对任务可靠性要求高、不允许任务丢失,且需及时感知任务提交失败的核心业务场景(如数据入库、订单处理)。
  • 使用示例:
1
2
3
4
5
6
ExecutorService pool = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 默认策略,可省略
);

2. CallerRunsPolicy(调用者运行策略)

  • 核心行为:新任务被拒绝后,由提交任务的线程(调用者线程)亲自执行该任务,不丢弃任务。
  • 特点:
    • 保证任务一定执行,除非线程池被关闭;
    • 可能阻塞调用者线程:若调用者为核心业务线程(如接口请求线程),执行任务会占用其资源,导致入口阻塞(如接口响应变慢)。
  • 适用场景:任务总量不大、对响应速度要求不高,且不允许任务丢失的非核心场景(如日志打印、数据备份)。

3. DiscardOldestPolicy(丢弃最旧任务策略)

  • 核心行为:先丢弃任务队列头部(最早提交但未执行)的任务,然后尝试将新任务提交到线程池执行。
  • 特点:会丢失队列中的旧任务,且无异常提示,可能导致关键任务丢失且难以排查。
  • 适用场景:任务具有时效性,新任务优先级高于旧任务,且允许丢弃旧任务的场景(如实时数据统计、临时缓存更新)。
  • 注意事项:建议搭配日志记录机制,跟踪任务丢弃情况,避免核心旧任务丢失。

4. DiscardPolicy(丢弃策略)

  • 核心行为:直接丢弃新提交的任务,不执行任何操作,也不抛出异常(空实现)。
  • 特点:任务无声丢失,排查困难,仅适用于任务无关紧要的场景。
  • 适用场景:任务丢失对业务无影响的场景(如非关键监控数据上报、临时日志采集)。
  • 禁忌场景:核心业务任务、数据一致性要求高的任务(绝对禁止使用)。