主题
界面
主题
线程池是并发编程的第一生产工具,但它的行为有一个反直觉的顺序:
core=2, queue=2, max=4,提交 7 个任务(各 500ms)实测走向:
任务 1、2 → 两个核心线程 ✓
任务 3、4 → **先入队**(哪怕 max 还没用满)✗←反直觉
任务 5、6 → 队列满了才扩容到 max(t3/t4)✓
任务 7 → 队列满 + 到 max → **拒绝** ✗✓★ 顺序是「core → 队列 → max → 拒绝」,不是「core → max → 队列」 ✓✓✓
★ 一个真实事故模式 ✗✗:
队列设成无界(LinkedBlockingQueue)→ 依赖变慢 → 任务堆积 → OOM ✗✗
而 max 设得再大也永远不生效(队列不满)✗✗✗
| 任务 | 去向 | 原因 |
|---|---|---|
| 1、2 | 核心线程 t1/t2 ✓ | core 未满 |
| 3、4 | 入队 ✗←反直觉 | core 满先入队 |
| 5、6 | 扩容线程 t3/t4 ✓ | 队列满才扩容 |
| 7 | 拒绝 ✗ | 队列满 + 到 max |
★ 推论 ✓✓✓:无界队列下 max 永远不生效 ✗✗
| 策略 | 行为 | 选型 |
|---|---|---|
| Abort(默认)✓ | 提交抛异常 | 调用方能感知 ✓ |
| CallerRuns ✓✓ | 提交者自己跑 → 背压降速 | 不能丢且想自动降速 ✓✓✓ |
| Discard ✗ | 静默丢 | 能丢的(日志/埋点)✓ |
| DiscardOldest ✗ | 丢队首最老 | 少用 ⚠️ |
★★★ 铁律:能丢的用 Discard;不能丢的必须 CallerRuns 或自定义落库重试 ✓✓✓
| 工厂 | 坑 |
|---|---|
| newFixedThreadPool(n) | 无界队列 → OOM ✗✗ |
| newCachedThreadPool() | max = Integer.MAX_VALUE → 线程爆炸 ✗✗ |
★ 正确姿势:手动 new ThreadPoolExecutor 显式四参数 ✓✓✓
| # | 纪律 |
|---|---|
| ① | 队列必须有界 ✓✓ |
| ② | 拒绝策略必须显式选 ✓✓ |
| ③ | 线程必须有名字(排查全靠它,《Arthas 与线上问题诊断》)✓✓✓ |
| ④ | 线程数按 IO/CPU 定:CPU 密集 = 核数;IO 密集 = 核数 × (1 + 等待/计算) ✗✓ |
★★★ 线程数的最后一句 ✓✓:虚拟线程时代(《虚拟线程》),IO 密集的池化将成为历史 ✓✓✓
// WORKER_SEQ 是静态 AtomicInteger;labs 的 ThreadPoolLesson.java 用同款写法(变量名 seq)
new ThreadPoolExecutor(8, 16, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
r -> new Thread(r, "order-worker-" + WORKER_SEQ.incrementAndGet()),
new ThreadPoolExecutor.CallerRunsPolicy());new ThreadPoolExecutor(8, 8, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(), // ✗✗ 无界
r -> new Thread(r), // ✗✗ 无名字
new ThreadPoolExecutor.DiscardPolicy()); // ✗✗ 丢任务无感知代码:labs/java-deep/src/main/java/cn/kb/learn/deep/concurrency/ThreadPoolLesson.java
实测输出(某次运行;线程领取顺序不固定):
task-7 REJECTED
task-2 by t2
task-5 by t3
task-1 by t1
task-6 by t4
task-4 by t2
task-3 by t4
routing: 1,2->core; 3,4->queue; 5,6->grow; 7->reject任务 7 的拒绝发生在提交阶段,通常最先打印;任务 3、4 是队列中的任务,由先空闲的核心线程领取,因此执行线程名会浮动;5、6 由扩容线程 t3/t4 执行。
| # | 错误 | 说明 |
|---|---|---|
| 1 | Executors 创建 ✗✗✗ | OOM 双坑 ✓✓✓ |
| 2 | 无界队列 ✗✗ | 堆积 OOM ✓✓ |
| 3 | 线程无名 ✗✗ | jstack 一团 thread-N ✓ |
| 4 | Discard 丢关键任务 ✗✗✗ | 静默丢 ✓✓ |
| 5 | IO 密集用 CPU 数量池 ✗ | 等待比公式 ✓ |
| 6 | 以为 max 先于队列 ✗✗✗ | 本节实测反直觉点 ✓✓✓ |
验收方式:给出任意参数组合,能推演 10 个任务的走向。
扩展任务:实现带超时的批量并行调用(invokeAll + 超时 + 部分结果处理)✓✓