← 返回软件技术总览 软件技术 · 知识点深化 · 线程池原理与七大参数:从核心线程到拒绝策略
软件技术 · 知识点深化 · 并发编程

线程池原理与七大参数:从核心线程到拒绝策略

线程池不是帮你"多开几个线程"那么简单。它是一个任务排队 + 线程复用的调度器:核心线程常驻、新任务进队列、队列满了才扩容到最大线程数、实在扛不住就拒绝。这一页把七个参数、四种拒绝策略、执行流程一条线讲透,面试时能徒手画出来。

① 怎么学(4 步走,约 70 分钟)

先建立直觉再抠细节,按这四步走最稳:

1看图建立直觉(10 分钟)
读②③:先在脑子里画出本课核心结构图。
2记完整体系(15 分钟)
读④:把对比表和公式看懂,不要急着背。
3跟例题走一遍(20 分钟)
精读⑤:看三个例题怎么用知识点解题。
4刷题纠错(剩余时间)
做⑦⑩,错题回⑥诊断。
本课小目标学完你要能:① 说出线程池七个参数含义;② 画出任务从提交到执行的完整流程;③ 说出四种拒绝策略及适用场景;④ 解释为什么不推荐 Executors 工厂方法。

② 一图看懂:线程池执行流程全景

线程池 核心线程 corePoolSize 常驻不回收 阻塞队列 workQueue 任务排队缓冲 最大线程 maxPoolSize 峰值扩容上限 拒绝策略 handler 兜底处理 应用:Web 服务器/异步任务 Tomcat/Nginx 后端通用 易错:队列无界导致 OOM newLinkedBlockingQueue 默认 Integer.MAX
读法:中心是本课主题,四条分支展开核心维度,下方是典型应用与高频易错点。

③ 本质直觉:线程池就是"共享办公室 + 取号机"

把线程池想成一个共享办公室:来了个任务(顾客),前台先看有没有空闲的核心员工(核心线程),有就直接接待。核心员工都在忙,就让任务去候客椅上排队(阻塞队列)。椅子也坐满了,临时招外包员工(非核心线程)直到人数上限(maxPoolSize)。连外包都招满了,就只能拒收(拒绝策略)。

为什么不用 new Thread()?线程创建销毁要花 OS 资源,每秒几万个请求就创建几万个线程,直接把机器打挂。线程池让线程复用:一个线程干完一个任务后不退出,继续从队列拿下一个。

关键顺序:核心线程→队列→最大线程→拒绝。很多人以为队列满了才扩容,对;但有人以为先扩到最大再排队,错——永远是先排队再扩容。

任务提交 核心线程空闲? 队列满?否→排队 扩容到 max外包线程 拒绝策略抛异常/降级
为什么先排队再扩容这是 Doug Lea 的设计哲学:队列能缓冲就缓冲,避免频繁创建销毁线程;只有队列真的满了才被迫加线程。顺序搞反会导致线程数暴涨。

④ 完整知识体系

七个参数与四种拒绝策略

参数/策略含义注意点
corePoolSize常驻核心线程数默认可以allowCoreThreadTimeOut
maximumPoolSize最大线程数必须≥corePoolSize
keepAliveTime空闲线程存活时间只针对非核心线程
workQueue任务缓冲队列有界队列防 OOM
threadFactory线程工厂给线程起名字便于排查
handler拒绝策略队列满+线程满时触发
Executors.newFixed固定线程数队列无界,可能 OOM
执行顺序任务来了 → 核心线程满了? → 入队 → 队列满了? → 扩容到 max → 还满? → 拒绝
CPU 密集型核心数 = N+1(偶尔页缺失停顿)
IO 密集型核心数 = 2N 或更多(线程大量时间在等待)

四种拒绝策略

AbortPolicy(默认):抛 RejectedExecutionException。
CallerRunsPolicy:谁提交谁执行,让提交线程自己跑,反压。
DiscardPolicy:悄悄丢掉新任务,不报错。
DiscardOldestPolicy:丢掉队列最老的任务,再尝试提交。

为什么阿里规范禁止 ExecutorsnewFixedThreadPool 和 newSingleThreadExecutor 用无界队列 LinkedBlockingQueue,任务堆积会 OOM;newCachedThreadPool 最大线程数是 Integer.MAX_VALUE,可能创建海量线程。必须手动用 ThreadPoolExecutor 构造。

⑤ 应用场景与例题

例1 核心线程2,队列3,最大线程4,来了6个任务
执行流程怎么走?
① 任务1、2 由核心线程执行;② 任务3、4、5 入队列;③ 任务6 队列满,创建非核心线程执行;④ 此时线程数=4=max。再来第7个任务就触发拒绝策略。
例2 CallerRunsPolicy 的作用
为什么背压场景喜欢用它?
队列和线程都满了之后,任务由提交它的线程(比如 Tomcat 的工作线程)自己执行。这会让提交变慢,等于告诉上游"我扛不住了,你慢点发",自然形成反压,避免系统被压垮。
例3 CPU 密集 vs IO 密集
8核机器做计算和做数据库查询分别配多少线程?
CPU 密集配 8~9 个,多了反而上下文切换浪费;IO 密集(大量等数据库/网络)可以配 20~50 个,因为线程大部分时间在阻塞,不占 CPU。
做题心法背参数不如背流程:核心→队列→max→拒绝,顺序一步都不能乱。

⑥ 高频错误诊断(4 条)

错误1:以为先扩容再排队实际是核心线程满了先入队,队列满了才扩容到 max。写反会导致线程数瞬间暴涨。
错误2:用无界队列new LinkedBlockingQueue() 默认容量是 Integer.MAX_VALUE,任务堆积直接 OOM。必须指定有界容量。
错误3:拒绝策略用 Discard 不报错线上任务悄悄丢了都不知道,至少用 Abort 打日志,或 CallerRuns 反压。
错误4:CPU 密集型配几百线程线程多了上下文切换开销反而大,CPU 密集型线程数 ≈ CPU 核数即可。

⑦ 考点真题演练(5 题)

考点分布

考法出题形式应对
执行流程画任务提交路径核心→队列→max→拒绝
参数含义问 keepAliveTime 作用控制非核心空闲回收
拒绝策略问 CallerRuns 行为提交线程自己跑
Executors 风险为什么禁止无界队列/最大线程无界

真题basic1. 线程池中任务提交后的处理顺序,正确的是?

真题mid2. ThreadPoolExecutor 的 keepAliveTime 作用于哪类线程?

真题mid3. CallerRunsPolicy 拒绝策略的行为是?

真题hard4. 阿里规范为什么禁止用 Executors.newFixedThreadPool?

真题hard5. 8核机器做 CPU 密集型任务,线程池大小建议?

⑧ 必背知识点卡

七参数:核心/最大/存活/队列/工厂/handler/时间单位
执行序:核心→队列→max→拒绝 顺序不能乱
队列:必须有界,防 OOM 别用无界 LinkedBlockingQueue
拒绝:Abort 默认抛异常 CallerRuns 反压最好用
CPU 密集:N+1 个线程 N=核数
IO 密集:2N 或更多 线程多数在等待
Executors:禁用,手动 ThreadPoolExecutor 阿里规范

⑨ 动手输出:给团队设计一个订单处理线程池

场景:下单后要异步发短信、加积分、推消息,QPS 峰值 500。
① 定性:这些任务都是 IO 密集(调外部接口),线程数可以多一些。
② 参数:核心 20,最大 50,队列容量 200,keepAlive 60 秒,线程工厂起名为 order-pool-。
③ 拒绝:用 CallerRunsPolicy,实在扛不住让 Tomcat 线程自己跑,形成反压。
④ 监控:埋点队列长度、活跃线程数、拒绝次数,超过阈值告警。
⑤ 降级:队列快满时直接返回"系统繁忙",不要让用户等超时。
口述思路"IO 密集就多配线程,但队列要有界、拒绝要反压、监控要跟上。"

⑩ 分层练习(基础 + 中档 + 拔高)

▍基础 6 题

基础1线程池七大参数里,常驻不回收的是?
核心线程 corePoolSize。
基础2任务先入队列还是先扩容?
先入队列,队列满了才扩容到 max。
基础3默认拒绝策略是哪个?
AbortPolicy,抛 RejectedExecutionException。
基础4CallerRunsPolicy 由谁执行任务?
提交任务的线程自己执行。
基础5为什么 CPU 密集型线程不宜太多?
多了上下文切换开销大,反而降低吞吐。
基础6Executors.newCachedThreadPool 的风险?
最大线程数 Integer.MAX_VALUE,可能创建海量线程。

▍中档 5 题

中档1keepAliveTime 对核心线程生效吗?
默认不生效;调用 allowCoreThreadTimeOut(true) 后才生效。
中档2有界队列选 ArrayBlockingQueue 还是 Linked?
Array 有界且预分配数组,性能稳定;Linked 可设定容量但节点对象有 GC 压力。
中档3线程池线程数设为 CPU 核数 N+1,为什么 +1?
偶尔页缺失或安全点停顿时,多一个线程保证 CPU 不空闲。
中档4DiscardOldestPolicy 丢的是哪个任务?
队列队头最老的任务,然后尝试重新提交。
中档5如何给线程池起名字方便排查?
自定义 ThreadFactory,命名前缀如 order-pool-。

▍拔高 5 题

拔高1核心线程数会被回收吗?
默认不会;allowCoreThreadTimeOut(true) 后超过 keepAliveTime 也会回收。
拔高2队列满了非核心线程干完任务后多久退出?
超过 keepAliveTime 且线程数 > corePoolSize 时退出。
拔高3Tomcat 的线程池和 JDK 有何不同?
Tomcat 先开线程到 max 再用队列(JDK 反过来),为了快速响应突发流量。
拔高4如何动态调整线程池参数?
暴露 corePoolSize/maxPoolSize 的 set 方法,结合配置中心热更新。
拔高5任务执行前如何做前置校验?
重写 beforeExecute/afterExecute 方法。

⑪ 记忆口诀 + 7 天复习计划

三句口诀① 核心队列最大拒,顺序一步不能错。② 有界队列反压跑,Executors 不能要。③ CPU 密集核数加一,IO 密集翻倍起。
天任务自检
第 1 天读②③④,画线程池执行流程图七参数能默写
第 2 天背执行顺序表 + 基础 6 题流程说不错
第 3 天做中档 6 题,手推一次6任务场景扩容时机对
第 4 天做拔高 5 题,写一个手动 ThreadPoolExecutor代码能跑
第 5 天做⑦真题 5 题每题限时 2 分钟
第 6-7 天合书口述拒绝策略四种不看资料全对

← 返回软件技术总览