线程池的五种状态
在 ThreadPoolExecutor 里面,线程池有 5 种状态,用 ctl 的高位(29位) 表示。
状态是按生命周期严格有序变化的!
一、线程池的五种状态
1. RUNNING(运行中)
- 定义:能接受新任务,同时也能处理队列中的任务。
- 特点:
execute()提交任务 ➔ 正常处理。- 核心状态。
- 标志值:
RUNNING = -1 << COUNT_BITS- 即高位全是 1(负数)。
2. SHUTDOWN(优雅关闭)
- 定义:不再接收新任务,但继续执行已提交(在队列中的)任务。
- 特点:
- 调用
shutdown()。 - 线程池不新建线程,只处理已有任务。
- 任务处理完后,自动进入下一个状态。
- 调用
- 标志值:
SHUTDOWN = 0 << COUNT_BITS
3. STOP(暴力关闭)
- 定义:不接收新任务,也不处理队列中的任务,中断正在执行的线程。
- 特点:
- 调用
shutdownNow()。 - 直接打断所有线程,清空工作队列。
- 调用
- 标志值:
STOP = 1 << COUNT_BITS
4. TIDYING(整理阶段)
- 定义:所有任务都结束(线程数为0,队列为空)后,进入清理阶段。
- 特点:
- 触发钩子方法
terminated()。
- 触发钩子方法
- 标志值:
TIDYING = 2 << COUNT_BITS
5. TERMINATED(终结状态)
- 定义:
terminated()方法执行完成后,线程池彻底终结。 - 特点:
- 彻底关闭,生命周期结束。
- 标志值:
TERMINATED = 3 << COUNT_BITS
二、五种状态的完整变化流程图
RUNNING
↓ shutdown()
SHUTDOWN
↓ 任务处理完毕
TIDYING
↓ terminated()
TERMINATED
或者异常暴力关闭:
RUNNING
↓ shutdownNow()
STOP
↓ 任务全部终结
TIDYING
↓ terminated()
TERMINATED
三、四种拒绝策略(RejectedExecutionHandler)
当线程池无法处理新任务时(比如:线程池满了 + 队列满了),就调用拒绝策略。
JDK 提供了 4 种标准实现,每一种都非常重要,面试爱问!
1. AbortPolicy(默认)
- 行为:
- 直接抛出
RejectedExecutionException。
- 直接抛出
- 特点:
- 不默默丢弃,立即失败报警。
- 适用场景:
- 重要业务,任务必须保证提交成功,否则立刻报错。
2. CallerRunsPolicy
- 行为:
- 谁提交的任务,谁自己执行(在调用
execute的线程里同步跑)。
- 谁提交的任务,谁自己执行(在调用
- 特点:
- 降低线程池压力,保护系统。
- 可能会让提交线程变慢,从而降低总系统压力。
- 适用场景:
- 可以接受降级处理的场景,比如后台日志系统。
3. DiscardPolicy
- 行为:
- 直接丢弃任务,不抛异常。
- 特点:
- 悄无声息地丢掉,业务无感知。
- 适用场景:
- 对任务可靠性要求不高,比如定时上报小数据。
4. DiscardOldestPolicy
- 行为:
- 丢掉工作队列里最老的任务,然后重新提交新任务。
- 特点:
- 保证新任务尽量进来,牺牲旧任务。
- 适用场景:
- 优先处理最新、最重要任务,比如流式消息处理。
【拒绝策略总结一句话版】
“抛异常(Abort) ➔ 自己跑(CallerRuns) ➔ 直接丢(Discard) ➔ 丢旧换新(DiscardOldest)”
拓展加分(如果想冲面试官印象的话)
可以加一句:
“实际生产中,很多团队会自定义 RejectedExecutionHandler,比如加日志报警、监控指标上报、队列持久化重试等方式,提升系统的可观测性与可靠性。”
更多详细内容请关注其他相关文章!