《Java 并发编程·线程与内存模型》8.1 CAS 原理与 ABA
从 compareAndSet 的 volatile 读写语义讲到失败重试的真实开销:用门闩固定交错,在无锁栈上把 ABA 复现成 2000/2000 次可重复的损坏,再用 AtomicStampedReference 压到 0/2000,并给出 AtomicMarkableReference 的 attemptMark 陷阱与本机实测输出。
tag
从 compareAndSet 的 volatile 读写语义讲到失败重试的真实开销:用门闩固定交错,在无锁栈上把 ABA 复现成 2000/2000 次可重复的损坏,再用 AtomicStampedReference 压到 0/2000,并给出 AtomicMarkableReference 的 attemptMark 陷阱与本机实测输出。
ScopedValue 用词法作用域取代线程绑定:绑定不可变、出了 run/call 自动解绑、结构化子任务自动继承、普通线程不继承。本机实测 JDK 25 上它已转正无需 preview,而 StructuredTaskScope 仍需 --enable-preview,附完整编译与运行输出。
InheritableThreadLocal 只在线程创建时复制一次快照,这个前提在线程池里不成立。实测同一个池线程连续服务三个请求,读到的都是第一个请求的会员号;父线程在创建后改值,子线程仍看到旧值。拆解串值、丢失、残留、半更新四种失败形态,给出方法参数、提交侧捕获、Executor 装饰器三种显式透传写法。
ThreadLocal 的哈希表、弱引用 key 与强引用 value 如何组合成一条泄漏路径。用反射读出 ThreadLocalMap 的 table 与 Entry 实测:6 个已失效的 ThreadLocal 仍留下 24 MiB 值,8 线程池不 remove 保留 32 MiB,remove 后为 0。
shutdown hook 是 JVM 退出前的最后一程,不是优雅停机的全部。本文用 50 次运行证明多个 hook 的启动顺序完全随机,实测异常不改变退出码、JVM 对 hook 没有超时、hook 里调用 System.exit 会卡死,并对比 SIGTERM、SIGKILL 与 Runtime.halt 的路径,最后给出 libraryd-conc 的单 hook 有序释放写法。
两阶段终止只有一个骨架:volatile 停止标志、interrupt 唤醒阻塞、join 限时等待。本文给出可直接使用的 Worker 完整类,在本机对比只发信号、立即中断、超时升级三条关停路径,并用真实输出说明 Future.cancel(true) 只是请求:任务不检查中断时,cancel 返回 true 也停不下来。
interrupt 不是杀死线程,只是把一个标志位置为 true,真正的退出仍要靠被中断线程自己检查。本文用 sleep、wait、join、synchronized 竞争与 ReentrantLock 在本机实测中断行为,说明 InterruptedException 为什么会清除标志、哪些阻塞根本不响应中断,并给出捕获后恢复标志再包装成 LoanException 的写法与边界。
从单锁 notifyAll 版写到 ReentrantLock 双条件版,补上关闭语义与中断处理,再和 ArrayBlockingQueue 正面对比。三个实现各跑 10 万元素全部有序正确,单锁版连续四次都在 39ms 以上,双条件版与 JDK 队列则贴着 20ms 上下波动。
wait 可能无缘无故返回,也可能醒来发现条件已被别人抢走,还可能信号早于等待而永久丢失。用两组实测对照说明:同一个双消费者场景里 if 版 100 轮全部取到 null,while 版 0 轮出错;而信号先发时裸 wait 等了 205ms 才超时。
wait 会释放监视器,notify 不会——两组实测时间线把这件事讲清楚,也说明不在同步块内调用为什么会抛 IllegalMonitorStateException。再给一个能跑的生产者消费者最小实现,讲清条件判断为什么必须写成 while、notify 与 notifyAll 该怎么选、被唤醒之后还要重新抢锁。
JIT 对 synchronized 做了哪些手脚:逃逸分析如何让方法内的锁消失、锁粗化如何合并循环里的加锁、偏向锁为什么在 JDK 25 已彻底移除。附 PrintFlagsFinal、UseBiasedLocking 报错与 LockingMode 弃用的本机真实输出。
用 javap 拆开 synchronized:同步方法靠 ACC_SYNCHRONIZED 标志,同步块生成 monitorenter/monitorexit 与异常表;再讲对象头 Mark Word 的锁状态、可重入计数,以及锁错对象、锁字符串常量这几类真实错误。
用 javap 与实测把 volatile 的三重语义拆开:它保证可见性与有序性,却不保证复合原子性;同步给出 DCL 的正确写法、8 线程下 volatile 计数器仍然丢更新的真实数据,以及 volatile 数组、volatile 引用与字段内容之间的边界。
解释 final 字段的初始化安全保证及其前提,用 ThisEscape 反例说明构造逸出如何让 final 失效;给出静态初始化、final 字段、volatile 引用、并发容器四类安全发布方式,并划清「引用可见」与「对象内部不可变」的边界。
《Java 服务端通信工程》全 50 篇:从 RPC 语义、Protobuf 编码与四种调用模式写起,覆盖 Stub/Channel、Interceptor、服务发现与负载均衡、连接池与 drain、重试预算、熔断、限流背压,再到 HTTP 网关、TLS 与租户授权、指标日志追踪、灰度发布与契约测试,最后以容量模型、故障演练和 linkd-v3 验收收尾,附完整章节索引与实验口径。
把 JMM 的八条 happens-before 规则逐条拆成最小代码,说明它们只约束可见性而非实际执行顺序;再用规则链推导双重检查锁定为什么必须加 volatile,给出四道「看到 A 是否一定看到 B」的推导练习与答案,并在借阅服务里用 volatile 发布不可变审计快照。
从一段停不下来的循环入手,拆开重排序、缓存可见性与有序性三个层次,说明 as-if-serial 只对单线程负责;给出无 volatile 与有 volatile 的停机标志完整程序及本机实跑输出,并说明这类 bug 为何依赖 JIT、CPU 与运行时长方能显现。
用 JCStress 0.16 把计数器竞态变成可重复的统计结果:完整给出 LostUpdate 与 AtomicCounter 两个测试类,解释五个注解、ACCEPTABLE/FORBIDDEN 与结果表读法,并贴出 Apple M1 Pro 上两次 quick 模式的真实输出,说明 FORBIDDEN 触发 AssertionError 是预期行为。
把同一个有竞态的借阅服务分别用不可变对象、synchronized、ThreadLocal 和 ConcurrentHashMap 改写,逐项比较代价与失效场景,并给出一张按共享可变性、状态大小和读写比选型的矩阵,说明并发容器的复合操作为何仍然不安全,最后给出单机、多实例与跨 key 场景下的取舍。
用一个会超卖的借阅服务拆解竞态的三种形态:check-then-act、read-modify-write 与发布逸出。给出完整的 UnsafeLoanService 和 32 线程复现程序,并解释为什么这类测试不稳定、跑绿也不能证明线程安全,以及库存没变负时超卖仍然可能发生。