《Java 并发编程·性能诊断与事故》目录与学习路径

《Java 并发编程·性能诊断与事故》全书导航:说明本卷在三卷本中的位置与五部分划分,给出顺序通读、症状驱动、值班查表三条路径,按部分列出 15 章 45 节的完整索引、每章三节的模型—诊断—处置递进、章间依赖与全书实验清单,并附一张从现象直接跳到章节的对照表。

这本书的结构

本卷 50 篇:序、前言、目录 3 篇,正文 15 章 45 节,附录 A/B 2 篇。

正文分五个部分,从「先能测量」推到「固化成规范」:

部分章主题读完能回答
一1–3性能模型、基准与诊断工具这台机器的上限在哪、数字可不可信、卡在哪一行
二4–6活性、容量与规模化为什么不走了、池该多大、虚拟线程能不能救
三7–9模型对比、正确性测试与可见性事故该不该换模型、怎么证明它是错的、读到旧值怎么办
四10–12队列线程事故、锁边界与可观测性积压从哪来、锁和事务怎么叠加、什么该报警
五13–15GC、容器与工程规范分配怎么影响停顿、容器里参数怎么给、怎么验收

五部分不是并列的。第一部分是度量能力:没有它,后面所有的「变慢了」「扛不住」都只是形容词。第二部分是急救,处理最紧急的三类现场。第三部分回到正确性,因为相当一部分「性能问题」最后查出来是可见性 bug。第四部分把前面的现象串成事故面。第五部分把结论固化成规范与基线。

每一节的组织方式

45 节的结构统一:

## 承接的问题      上一节留下了什么问题,这一节要解决它
## 核心机制        2–4 个 h2,每个先给证据或模型,再解释
## 在 libraryd-conc 里落地
## 常见错误与边界  什么时候不要用、这个方法的代价
## 小结
(末尾一行上一节 / 下一节的双向链接)

三个特点值得提前说明:

先给证据,再给解释。 不是先讲道理再补一个实验,而是先让你看到 JFR 记录、线程转储或压测曲线,再解释它为什么长这样。你可以先自己猜一遍。

每节都有一个可重放的现场。 要么是一段能跑的复现代码,要么是一条能复现现象的命令。构造场景的参数写全,你可以在自己机器上原样重放。

每节都写「什么时候不要用」。 讲一个工具能干什么容易,讲清它的代价和失效条件难。本卷每节至少给一个不该用的场景。

三条学习路径

路径一:顺序通读(约 35 小时)

从 1.1 读到 15.3,按章节顺序。适合要负责线上稳定性、正在做容量规划的人。

部分章建议时长难点
一1–310 小时第 1 章的排队论直觉,第 3 章的 JFR 阈值
二4–69 小时第 5 章的容量公式与假设,第 6 章的 pinning 判定
三7–99 小时第 8 章的 Lincheck(未在本机执行,读起来最抽象)
四10–125 小时第 11 章的分布式锁边界
五13–152 小时主要是参数表与清单,可快速过

顺序通读的前提是读过卷一,尤其是第 3 章(happens-before)和第 9 章(ReentrantLock)。卷二的源码细节不是必需,但第 3、4 章会回指卷二讲过的排队与唤醒路径,没读过时那些句子可以跳过。

路径二:症状驱动(按需)

带着具体现象来的人,用这张表直接跳到对应章节。表按现象组织,不按知识点。

你遇到的现象去读能得到的答案
并发上去了,吞吐不涨反降1.1、1.3串行比例与一致性延迟,扩展性天花板在哪
CPU 利用率不高,RT 却很高1.2、3.3线程在等什么,转储里怎么看
同样的代码,P99 每次压测都不一样2.1、2.3基准的误差来源,什么时候该信数字
微基准显示很快,线上却很慢2.2、2.3死代码消除、常量折叠,以及微基准回答不了什么
线程转储里一片 BLOCKED (on object monitor)3.1、3.3锁竞争的热点在哪,谁持有
想看火焰图但不知道怎么读3.2锁视图与分配视图的读法(工具未装,给命令)
进程卡死,日志不动,CPU 为 04.1死锁的四个条件与 JVM 自动检测
线程都在跑,但业务没有进展4.2活锁、饥饿与重试碰撞
加锁顺序不统一,偶发卡死4.3全局锁序规范与超时兜底
线程池该开多大,全靠拍脑袋5.1、5.2L = λW 与 N×(1+W/C) 的用法和假设
队列满了以后大量请求被拒5.3、10.2拒绝率曲线,以及拒绝策略怎么放大故障
想用虚拟线程,但不知道能开多少6.110 万与 100 万的实跑口径与堆占用
虚拟线程吞吐上不去,载体线程被占满6.2pinning 的剩余场景与 JFR 判定
虚拟线程里 MDC / ThreadLocal 丢了6.3上下文传递与连接池限流
团队在争论要不要改成响应式7.1、7.2两种模型的取舍矩阵
混合栈里 trace 断了7.3阻塞调用与线程池隔离的边界
怀疑有竞态,但复现不出来8.1、8.3JCStress 的测试模型与最小化方法
想知道某个容器是不是线性一致的8.2Lincheck 的检查方式(未在本机执行)
一个线程读到了另一个线程写的旧值9.1、9.3过期读的成因与 happens-before 归因
对象还没构造完就被别的线程看到9.2构造逸出、final 语义与安全发布
堆一路涨,最后 OOM10.1无界队列堆积的判定与堆分析
一次超时引发全站雪崩10.2AbortPolicy 与重试放大的叠加
线程数只增不减,元空间也在涨10.3线程泄漏与 TCCL 持有类加载器
数据库死锁日志和 Java 锁同时出现11.1行锁与 Java 锁的顺序叠加
分布式锁偶尔失效11.2租约、时钟、看门狗与 fencing token
一把锁成了全站瓶颈11.3热点定位与锁拆分、分段
不知道该监控线程池的哪几个数12.1五个指标口径与采集代码
知道慢,但不知道慢在哪个调用12.2阻塞点与 trace 上下文结合
告警天天响,没人看12.3阈值、分位与容量水位
分配速率高,GC 频繁13.1TLAB、逃逸分析与分配采样
GC 停顿和业务毛刺同时出现13.2并发标记的写屏障开销
想要低延迟,不知道从哪下手13.3分配控制、对象复用与缓存行
容器里 availableProcessors() 不对14.1CPU 配额与 ActiveProcessorCount
容器被 OOMKilled,堆却没满14.2-Xss、MaxRAMPercentage 与非堆内存
K8s 里并发参数怎么给14.3requests/limits 与池大小的基线表
团队代码风格各异,问题反复出现15.1、15.2规范条目与 20 条 Review 清单
上线前要出一份并发验收报告15.3场景、指标、通过判据、遗留风险

路径三:值班查表(工具书用法)

半夜被叫起来,没时间读整章。先看手上有什么,再翻对应的一张表。

手上有什么先看再看
一份线程转储3.3 转储读法4.1 死锁判定、3.1 锁热点
一段 JFR 记录3.1 事件与阈值6.2 pinning、13.2 GC 事件
一组监控曲线12.1 指标口径12.3 告警阈值、5.3 拒绝率
一个新出的堆转储10.1 队列堆积10.3 类加载器泄漏
一份压测报告2.3 微基准与系统压测的差别1.3 分位数口径
一段可疑代码9.3 happens-before 归因15.2 Review 清单

分部分章节

第一部分:性能模型、基准与诊断工具(第 1–3 章)

这一部分回答一个问题:在动手之前,怎么知道「慢」慢在哪、有多慢、数字可不可信。

节标题一句话
1.1Amdahl 与 USL:扩展性的天花板串行比例和一致性延迟如何决定加机器还有没有用
1.2上下文切换、调度延迟与 CPU 配额vmstat 与 pidstat 里哪些数字是真的
1.3吞吐、时延与并发的三角平均值为什么一定会骗人,尾延迟从哪来
2.1JMH 的正确姿势@State 作用域、@Fork、Blackhole 与误差
2.2微基准的陷阱死代码消除、常量折叠、循环展开、预热
2.3从微基准到系统压测微基准能回答什么,永远回答不了什么
3.1JFR 的 Monitor 事件阈值不调等于没录,一次真实的锁竞争记录
3.2async-profiler 的锁与分配火焰图安装、-e lock、-e alloc 与读图方法
3.3线程转储的读法jcmd Thread.print -l 里哪几行有信息量

第二部分:活性、容量与规模化(第 4–6 章)

这一部分处理最紧急的现场:不走了、池不够、线程不够。

节标题一句话
4.1死锁的四个必要条件与检测构造一个死锁,让 JVM 自己报出来
4.2活锁、饥饿与优先级反转线程都在跑却不前进,怎么证明
4.3锁顺序规范与超时兜底全局锁序、tryLock 超时、分层锁
5.1Little’s law 在池化中的应用L = λW 怎么变成一张容量表
5.2CPU 密集与 IO 密集的池大小N+1 与 N×(1+W/C) 的假设和校核
5.3队列长度、拒绝率与降级有界队列把「慢」换成「失败」的那条曲线
6.1十万/百万虚拟线程实测创建耗时、堆占用、载体线程数
6.2pinning 的诊断与消除JDK 25 剩余的 pin 场景与 JFR 判定
6.3连接池、ThreadLocal 与 MDC虚拟线程不解除下游限制,用 Semaphore 兜住

第三部分:模型对比、正确性测试与可见性(第 7–9 章)

这一部分回答:该不该换模型,以及怎么证明一段并发代码是错的。

节标题一句话
7.1Reactor 的线程模型回顾事件循环、非阻塞与背压的概念对照
7.2两种模型的取舍矩阵团队、调试、生态、阻塞调用四个维度
7.3混合栈的边界与调试阻塞调用怎么隔离,trace 怎么不断
8.1JCStress 的测试模型状态、actor、arbiter 与结果分级
8.2Lincheck 的线性一致性检查线性化点、场景生成与报告(未在本机执行)
8.3竞态复现、最小化与回归缩小状态、固定种子、进 CI
9.1过期读与无限循环JIT 提升导致的可见性问题与 volatile 修复
9.2安全发布失败的对象构造逸出、部分初始化与 final 语义
9.3从事故到 happens-before 归因归因表、证据链与修复验证

第四部分:队列线程事故、锁边界与可观测性(第 10–12 章)

这一部分把现象串成事故面:积压、雪崩、泄漏、锁叠加、告警。

节标题一句话
10.1队列堆积与 OOM无界队列 + 生产快于消费的完整现场
10.2拒绝策略引发的雪崩AbortPolicy 与上游重试怎么放大故障
10.3线程泄漏与上下文类加载器线程不退出、TCCL 持有、类加载器泄漏
11.1数据库行锁与 Java 锁的叠加两把锁的顺序、事务边界与连接池
11.2分布式锁的租约与续期租约、时钟、看门狗与 fencing token
11.3锁粒度、热点与拆分分段、分片、无锁化的取舍
12.1线程与池的指标口径active/pool/queue/completed 与拒绝数
12.2阻塞点与调用链结合阻塞耗时怎么和 trace 对上
12.3告警阈值与容量水位分位、饱和度与告警分级

第五部分:GC、容器与工程规范(第 13–15 章)

这一部分把前面所有结论固化成参数、规范和验收标准。

节标题一句话
13.1分配速率、TLAB 与逃逸谁在分配、分配多快、能不能不分配
13.2并发标记与业务线程写屏障的开销藏在哪
13.3低延迟 GC 下的并发设计分配控制、对象复用与缓存行
14.1availableProcessors 与 CPU 配额容器感知、ActiveProcessorCount、默认池大小
14.2内存限制、线程栈与容器 OOM-Xss、MaxRAMPercentage 与 OOM 类型
14.3K8s 下的并发参数基线requests/limits、池大小、探针
15.1编码规范与静态检查命名、锁顺序、禁止项与检查工具
15.2Code Review 清单20 条,按后果分级
15.3libraryd 并发验收与压测报告场景、指标、通过判据、遗留风险

附录不在正文序列里,按需查阅:

附录内容
附录 A 诊断命令速查jcmd、jstack、jfr、vmstat 的命令表
附录 B JCStress/JMH/压测模板三个可直接复制的模板

每章三节怎么递进

同一章的三节按「模型 → 诊断 → 处置」排,不是三个并列的知识点。

章模型诊断处置
1 性能模型Amdahl/USL 算上限上下文切换与配额吞吐与时延的三角
2 基准压测JMH 怎么写微基准的陷阱从微基准到系统压测
3 诊断工具JFR 事件async-profiler 火焰图线程转储
4 活性死锁四条件活锁与饥饿锁顺序与超时兜底
5 容量Little’s law池大小的两种公式队列与拒绝降级
6 规模化十万/百万线程pinning连接池与上下文
7 模型对比Reactor 线程模型取舍矩阵混合栈边界
8 正确性测试JCStress 模型Lincheck 线性一致竞态最小化与回归
9 可见性事故过期读安全发布失败happens-before 归因
10 队列线程事故堆积成因拒绝雪崩线程泄漏与 TCCL
11 锁边界行锁与 Java 锁叠加分布式租约锁粒度拆分
12 可观测性指标口径阻塞与调用链告警阈值
13 GC分配与 TLAB并发标记开销低延迟设计
14 容器CPU 配额内存与线程栈K8s 基线
15 规范编码规范Review 清单验收报告

章间依赖

1 性能模型 ── 2 基准 ── 3 诊断工具
3 ──┬── 4 活性
    ├── 5 容量
    └── 10 队列事故
4 / 5 / 10 ── 11 锁边界 ── 12 可观测性
12 ── 13 GC ── 14 容器 ── 15 验收

6 规模化:依赖 1、5;7 模型对比:独立,可先读
8、9 测试与可见性:依赖卷一第 3 章

几条硬依赖:

  • 第 3 章的三节都假设你读过第 1、2 章。不知道「这台机器的上限」和「数字有多大误差」,读到 JFR 记录时会误判量级。
  • 第 5.2 节的池大小公式需要 5.1 的 L = λW 做铺垫,跳过 5.1 会看不懂「为什么先算并发度再算线程数」。
  • 第 10、11 章反复引用第 4 章的锁分析。第 4 章没读,就只能记住结论。
  • 第 15.3 节的验收报告是前面全部章节的汇总,最后读,或者在做验收前单独读。
  • 第 8 章依赖卷一的 JMM,第 9 章依赖卷一的 happens-before 规则。本卷不重推,只回指。

全书实验清单

章实验用什么能复现什么
1并发阶梯压测纯 Java + conc-ops吞吐随并发变化的拐点
2错误与正确的 JMH 写法JMH 1.37死代码消除、常量折叠
3锁竞争 JFRJFR(阈值 0 与 20 ms 对照)阈值如何改变结论
4两个线程交叉加锁纯 JavaJVM 打印 Found one Java-level deadlock
5两种任务的池大小纯 Java + JMHCPU 密集与 IO 密集的最优线程数差异
610 万 / 100 万虚拟线程纯 Java + JFR创建耗时、堆占用、pinning
7同一业务的两种写法对照纯 Java(不引 Reactor)阻塞调用在事件循环里的后果
8JCStress 竞态验证JCStress 0.16不允许出现的结果一次都没有
9过期读构造纯 JavaJIT 提升导致的无限循环
10无界队列堆积纯 Java + 堆转储OOM 前的堆形状
11锁顺序反转纯 Java + 数据库(可选)行锁与 Java 锁叠加的等待环
12指标采集端点conc-ops 8091五个口径的实时读数
13分配速率对比JFR ObjectAllocationSample分配速率与 GC 频率的关系
14不同 CPU 配额下的默认值容器或 -XX:ActiveProcessorCountavailableProcessors() 的来源
15完整验收压测conc-ops通过判据是否满足

所有实验都在 conc-lab 与 conc-ops 里,Maven 配置见前言。第 3.2 节的 async-profiler 实验本机未执行,只给命令;第 8.2 节的 Lincheck 实验未在本机执行,只给依赖与最小示例。

与卷一、卷二的关系

卷一《线程与内存模型》卷二《JUC 源码与容器》卷三(本卷)
回答怎么写得对为什么这样实现怎么压、怎么查、怎么救
典型问题这段代码有竞态吗这个类为什么这么设计线上 RT 抖了怎么定位
前置无卷一第 1–13 章卷一、卷二主干

本卷会频繁回指前两卷,形式是「卷一 3.2 已讲」「卷二 1.2 已讲」。遇到这种句子,没读过可以跳过,不影响本节结论;想补齐推导链再回去读。

反过来,涉及压测方法、指标口径、容器参数的地方,卷一和卷二只写「卷三第 N 章展开」,不重复。

五个常见误区

误区一:CPU 不满就不是性能问题。 恰恰相反,并发系统的默认状态是「等」。CPU 30% 而 P99 涨 30 倍,是本卷最常见的一类现场。

误区二:平均值能代表体验。 平均值把长尾抹平了。1000 个请求里 990 个 10 ms、10 个 2 s,平均值是 30 ms,而 1% 的用户在骂人。

误区三:线程池越大越保险。 线程数超过「核数 ×(1 + 等待比)」之后,多出来的线程只增加切换和内存,不增加吞吐。

误区四:换成虚拟线程就万事大吉。 虚拟线程解除的是「线程数」这个限制,不解除连接池、数据库、下游 QPS 的限制。第 6.3 节整节讲这件事。

误区五:调大队列能缓解拒绝。 它只是把拒绝推迟,同时把排队时间加长、把内存占用推高。第 5.3 节与 10.1 节各有一半的篇幅在讲这个。

版本与数据口径

  • 全部代码用 Temurin 25.0.5+8、--release 25。ScopedValue 已转正;StructuredTaskScope 仍是 preview,出现时必带 --enable-preview。
  • 本机是 Apple M1 Pro、10 核、32 GB,写作期间 load average 常在 70–170。所有墙钟时间标注「单次运行、随调度波动、不作基准」。
  • async-profiler 未安装,Lincheck 未在本机执行。这两处只给命令与依赖,不贴输出。
  • 所有事故都是构造场景,参数写全,可以在自己机器上重放。

读完本书之后

如果 45 节都读完了,你应该能做到三件事:拿到一份线程转储,能在五分钟内说出线程卡在哪类等待上;面对一个「并发上不去」的问题,能先问「是串行比例、是排队、还是锁竞争」;做容量规划时,能写出假设、公式和余量系数,而不是拍一个数字。

接下来有两条路。一条是往下钻 JVM:GC 的实现、JIT 的优化、内存布局,第 13 章只是开了个头。另一条是往外扩:数据库、消息队列、服务网格各自的并发模型,第 11 章只讲了边界。

无论走哪条,建议保留一个习惯:先要证据,再下结论。本卷每一节都是这么写出来的,你读的时候也可以拿这条标准来检验它。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《Java 消息中间件·事件驱动架构》附录 B 架构决策记录模板
  2. 《Java 消息中间件·事件驱动架构》附录 A 术语表
  3. 《Java 消息中间件·事件驱动架构》15.3 全书验收