0%

java | ThreadPoolExecutor 用法

线程池状态

ThreadPoolExecutor 使用 int 的高 3 位来表示线程池的状态,低 29 为表示线程池数量。「之所以以一个 int 来记录两个信息,而不是用两个 int 记录,是因为,使用一个 int 可以用 cas 原子操作进行赋值」

状态名 高3位 接收新任务 处理阻塞队列任务 说明
RUNNING 111 Y Y
SHUTDOWN 000 N Y 不会接收新任务,但会处理阻塞队列剩余任务
STOP 001 N N 会终端正在执行的任务,并抛弃阻塞队列的任务
TIDYING 010 - - 任务全部执行完毕,活动线程为 0 即将进入终结
TERMINATED 011 - - 终结状态

构造方法

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
  • corePoolSize
    • 核心线程数目「最多保留的线程数」
  • maximumPoolSize
    • 最大线程数目
      • 救急线程数等于 = maximumPoolSize - corePoolSize
      • 救急线程可以执行突发任务,比如任务队列选择有界队列,任务的数量超过了队列大小,就会创建 maximumPoolSize - corePoolSize 数目的救急线程来救急
  • keepAliveTime
    • 生存时间 - 针对救急线程
  • unit
    • 时间单位 - 针对救急线程
  • workQueue
    • 阻塞队列
  • threadFactory
    • 线程工厂 - 可以为线程创建时起个好名字
  • handler
    • 拒绝策略

关于上述的用法可以参考 自定义阻塞队列

如果线程达到 maximumPoolSize 任有新任务,则会执行拒绝策略,拒绝策略由 JDK 提供了 4 种实现

  • AbortPolicy
    • 让调用者抛出 RejectedExecutionException 异常,这是默认策略
  • CallerRunsPolicy
    • 让调用者运行任务
  • DiscardPolicy
    • 放弃本次任务
  • DiscardOldestPolicy
    • 放弃队列中最早的任务,本任务取而代之

除此之外,其他框架的实现如下

  • Dubbo
    • 在抛出 RejectedExecutionException 异常前会记录日志,并 dump 线程栈信息,方便定位问题
  • Netty
    • 创建一个新的线程执行任务
  • ActiveMQ
    • 带超时等待(60s)尝试放入队列
  • PinPoint
    • 使用了拒绝策略链,会逐一尝试策略链中每种拒绝策略

当高峰过去后,超过 corePoolSize 的救急线程,如果有一段时间没有任务做,需要结束节省资源,这个时间由 keepAliveTimeunit 控制。

根据这个构造方法,JDK Executors 类中提供了众多工厂方法来创建各种用途的线程池。

请我喝杯咖啡吧~