《Java 并发编程·性能诊断与事故》目录与学习路径
《Java 并发编程·性能诊断与事故》全书导航:说明本卷在三卷本中的位置与五部分划分,给出顺序通读、症状驱动、值班查表三条路径,按部分列出 15 章 45 节的完整索引、每章三节的模型—诊断—处置递进、章间依赖与全书实验清单,并附一张从现象直接跳到章节的对照表。
tag
《Java 并发编程·性能诊断与事故》全书导航:说明本卷在三卷本中的位置与五部分划分,给出顺序通读、症状驱动、值班查表三条路径,按部分列出 15 章 45 节的完整索引、每章三节的模型—诊断—处置递进、章间依赖与全书实验清单,并附一张从现象直接跳到章节的对照表。
《Java 并发编程·性能诊断与事故》前言:钉死本机的 JDK、Maven 与工具版本,交代 load average 常年 70–170 对计时的影响,给出 JMH、JCStress、JFR 的可用命令与实测事件名,说明 async-profiler 未安装、Lincheck 未执行,并定义本卷四类数据标注与五个指标口径。
《Java 并发编程·性能诊断与事故》开篇:从一个 CPU 只有三成、GC 正常、日志无异常却 P99 涨了三十倍的构造场景出发,分清正确性、性能、容量三类失效,给出从症状到验证的五步诊断闭环,交代三卷边界与本卷构造事故的口径,并说明为什么本书的数字只讲结构性差异。
《Java 并发编程·性能诊断与事故》全 50 篇:从 Amdahl 与 USL 的性能模型、JMH 与系统压测、JFR 与线程转储,到死锁活锁、池容量与虚拟线程、JCStress 与 Lincheck、可见性缺陷、队列 OOM、线程与 TCCL 泄漏、跨资源锁、指标告警、GC 与分配、容器配额与 CFS 节流,最后落在编码规范、评审清单与验收报告,附本机实测输出。
从 JDK 25 的 src.zip 解压开始,给出 AQS、ReentrantLock、ConcurrentHashMap、ThreadPoolExecutor、ForkJoinPool 的依赖阅读顺序;用四组本机 javap 输出说明该看什么,再用 --add-opens 反射、jstack、jcmd 线程转储与 JFR 对齐源码字段与运行态。
把本卷涉及的 62 个 java.util.concurrent 类型按锁与同步器、并发容器、阻塞队列、原子类、执行器、异步编排六类摊开,逐类给一句话定位与对应章节;用 ASCII 分类树和有序性、读写比例、迭代语义、容量语义、内存放大、能否阻塞六个维度的选型表,最后落到借阅系统的具体选型。
把前十四章的结论压成 20 条可执行的检查项,按数据错误、死锁、性能、可维护性分级,每条给出症状、正确做法和对应章节;再配一个能在仓库里跑的 grep 扫描脚本、CI 闸门和一条 P0 的完整评审样例,贴出脚本在示例代码上的真实输出与它扫不出来的七件事。
线程安全的容器只保证单个方法原子,不保证两次调用之间不被插入。本节用 ConcurrentHashMap 的「先查后改」复现库存超卖,贴出 12 次运行的真实超卖数与 compute 修复后的零超卖,再说明 compute 的桶锁边界、synchronizedMap 的复合操作、弱一致迭代与快速失败的区别。
用 javap 把 new 拆成分配、构造、发布三步,说清双重检查锁定为什么字段必须 volatile、它挡的是哪条重排;再给出 VarHandle 的 acquire/release 版本、静态持有者的单条 getstatic 与枚举的反射边界,并用一个 this 逃逸的构造稳定复现未初始化对象被发布。
用 Semaphore 加租约表实现有界资源池,靠 availablePermits 与持有时间两条线索抓出忘记 release 的泄漏;对照公平与非公平的排队延迟,用 reducePermits 做动态缩容,并说清令牌桶限速率、信号量桶限并发的区别。
用数量阈值加时间阈值做批量窗口,用 drainTo 一次持锁取走整批并唤醒阻塞的生产者;再用有界队列和 CallerRunsPolicy 演示背压的真实形状:40 条里 7 条被拒、调用线程亲自执行任务,并给出批量失败后的二分重投边界与幂等前提。
用 System.nanoTime 算剩余预算,把单次超时与指数退避都裁剪到预算之内;用 JDK 25 的 orTimeout 源码说明它为什么不取消底层任务;用 AtomicReference 加 AtomicLong 实现 closed/open/half-open 三态熔断,并贴出本机真实的状态迁移日志与预算消耗。
异常为什么被包装成 AltResult 而不是单独存一个字段?join 抛 CompletionException、get 抛 ExecutionException,这条边界在源码里画在哪?本节从 encodeThrowable、encodeRelay 讲到 cancel 与 orTimeout,并用异常链实验说明回调里拿到的异常类型为什么不一致。
thenApplyAsync 不传执行器时落到哪个池?为什么 thenApply 不一定在完成者的线程上跑?本节拆开 ASYNC_POOL 与 commonPool 的关系、SYNC/ASYNC/NESTED 三档模式,用自定义执行器与占满并行度的实验说明阻塞调用为什么会卡住整条链。
thenApply 返回的 future 没有线程在等它,为什么 complete 一调用整条链就醒了?本节从 result 与 stack 两个 volatile 字段入手,拆解 Treiber 栈的压栈与倒栈、NESTED 模式的迭代展开,并用反射读出真实链上的 UniApply、UniCompose、UniRelay、CoCompletion。
并行流的性能由拆分决定:Spliterator 的 trySplit/estimateSize/characteristics 三个契约,AbstractTask 与 CountedCompleter 的循环式拆分、LEAF_TARGET 与目标叶大小,commonPool 固定并行度的代价;附本机拆分次数、栈溢出与阻塞任务实测,以及自定义 Spliterator 的两版求和。
JDK 25 的 WorkQueue 两端为什么会在同一格上撞:poll() 的 CAS 路径、base 一致性检查与 onSpinWait 的停滞判断,phase 如何用 IDLE 位同时充当提交队列的 seqlock 与版本号,nsteals 与 getStealCount 的统计口径,附本机字节码、phase 原始值和高并发窃取失败率数据。
拆开 JDK 25 的 ForkJoinPool$WorkQueue:base/top/array/config/phase 五个字段的职责,owner 走 nextLocalTask(fifo)、窃取者走 poll(),pollScan 的随机起点与 submissionsOnly,以及 getSurplusQueuedTaskCount 为何能压低窃取;附本机 javap 与对照实验。
DelayedWorkQueue 用数组堆加 heapIndex 反查实现 O(log n) 取消,用 leader 与 available 保证同一时刻只有一个线程带超时等待。本节逐分支拆 take(),用四个 worker 等一个到期任务的本机输出证明只有 leader 被唤醒,并对比 DelayQueue 的容量与删除代价。
一个任务如何变成一个新线程、线程如何取任务又如何退出。本节拆 addWorker 的抢名额与回滚、runWorker 的 firstTask 加 getTask 循环,并用本机实测展示 ThreadFactory 返回 null 时任务被静默入队、beforeExecute 抛异常时任务不执行但 completedTaskCount 仍然加一。