最近系统地把《深入理解 Java 虚拟机》第四版》的并发相关章节重新读了一遍。
必须说,这本书依然是 JVM 领域最扎实的一本。

但是当我带着 JDK21 的视角再看“高效并发”这一部分时,脑子里反复出现一个感觉:

书里讲的都对,但时代变了。

今天我只聊三块内容:

  • 线程模型

  • 锁与同步

  • 并发设计思想

不谈 GC,不谈类加载。


一、线程模型:从 1:1 到 N:M,这是“地基级变化”

书里讲线程的时候,默认前提是:Java 线程 = 操作系统线程

也就是 1:1 模型。

这在 JDK8 时代完全正确:

  • 每创建一个 Thread,就创建一个内核线程

  • 每个线程默认 1MB 栈空间

  • 上下文切换是内核调度

  • 阻塞会挂起 OS 线程

所以当时我们学并发时,潜意识里有一句话:

线程是重资源。

这直接影响了整套工程实践:

  • 必须用线程池

  • 线程数不能太多

  • IO 必须用 NIO

  • 必须避免阻塞

但是 JDK21 引入了 虚拟线程(Virtual Thread)

模型变成:Java 虚拟线程 → 由 JVM 调度 多个虚拟线程 → 复用少量 OS 线程

也就是 N:M。

这意味着什么?

  • 线程创建成本极低

  • 栈不再固定 1MB

  • 阻塞时可以从 carrier 线程卸载

  • 可以开几十万线程

这不是“优化”。

这是把并发模型从内核态搬到了用户态。

如果用一句话总结:

JDK8 时代在优化“线程数量”,JDK21 时代开始优化“线程结构”。


二、阻塞不再是罪过:工程哲学被改写

书里大量强调:

  • 减少线程上下文切换

  • 避免阻塞

  • 使用异步非阻塞模型

在 JDK8 时代这是对的。

因为阻塞意味着:

  • 占用一个 OS 线程

  • 线程池耗尽

  • 系统崩溃

于是我们写出了大量这样的代码:

CompletableFuture .thenApply() .thenCompose() .thenAccept()

或者 Reactor:

Mono.flatMap().map().subscribe()

我们写复杂的回调链,不是因为喜欢函数式,而是因为:

线程太贵。

但在 JDK21 时代:

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { ... }

或者:

Executors.newVirtualThreadPerTaskExecutor()

你可以用同步阻塞写法,写出类似异步的吞吐能力。

这件事非常关键。

因为它改变了一个核心问题:

现在的重点不再是“如何避免阻塞”,而是“如何避免共享”。


三、锁的意义变了:从减少竞争到减少共享

书里对 synchronized、CAS、volatile、AQS 的讲解依然非常重要。

  • 偏向锁

  • 轻量级锁

  • 锁膨胀

  • 自旋

  • CAS 自旋失败退化

但你要知道一件事:

JDK21 已经移除了偏向锁。

原因很简单:

  • 当年的 Web 场景:单线程访问对象多

  • 现在的微服务:线程短命、高并发、切换频繁

  • 偏向锁反而成为负担

也就是说:

JVM 已经承认当年的锁优化策略不再主流。

更重要的是,在虚拟线程时代:

锁竞争更容易出现。

因为你可能创建 10 万个线程去访问共享对象。

所以问题从:如何优化锁

变成:如何减少共享

这是并发设计思想的转移。


四、Monitor 与 synchronized 依然重要,但语境变了

书里讲 Monitor 结构:

  • ObjectMonitor

  • EntryList

  • WaitSet

  • Owner

这些依然成立。

但虚拟线程带来一个新问题:

pin

当虚拟线程在 synchronized 内阻塞时:

  • 它不能卸载

  • 会占用 carrier 线程

  • 导致线程池资源被锁死

这意味着:

在虚拟线程场景下,synchronized 可能比 ReentrantLock 更“危险”。

因为 AQS 支持 park/unpark。

所以:

书里的 Monitor 机制没有错,但使用语境已经发生变化。


五、JMM 没变,但实践重点变了

Java 内存模型(happens-before、内存屏障、volatile)没有改变。

lock addl $0x0,(%esp) 作为内存屏障的原理依然成立。

CPU 的缓存一致性协议依然是 MESI。

但在 JDK21 时代,真正的热点已经转向:

  • Structured Concurrency

  • ScopedValue

  • Structured cancellation

  • ForkJoinPool 与 carrier 线程调度

也就是说:

内存模型没变,但并发抽象层升级了。


六、结构化并发:这是书里完全没有的世界

《深入理解 Java 虚拟机》第四版成书时,结构化并发还不存在。

而在 JDK21:

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { ... }

你可以:

  • 自动取消子任务

  • 聚合异常

  • 控制任务生命周期

  • 保证父子线程结构一致

这在 JDK8 是做不到的。

我们当年写的是:

  • Future + get

  • CountDownLatch

  • 自己管理取消

现在是语言级并发结构。

这是一种范式升级。


七、总结:JDK8 vs JDK21 并发的根本区别

维度 JDK8 世界 JDK21 世界
线程 重资源 轻资源
模型 1:1 N:M
重点 降低线程数 降低共享
异步 必须 可选
阻塞 危险 可接受
锁优化 偏向锁、自旋 减少共享、避免 pin
并发结构 手动组合 结构化并发

一句话总结:

JDK8 是“线程稀缺时代”的工程学
JDK21 是“线程廉价时代”的设计学


八、那这本书还值得读吗?

非常值得。

因为:

  • Monitor 原理没变

  • CAS 原理没变

  • JMM 没变

  • AQS 没变

  • CPU 内存模型没变

变的是:

上层并发抽象。

就像学操作系统一样。

你必须理解底层,
但工程实践一定要跟上时代。


九、我的个人建议(给正在读这本书的人)

如果你现在是 JDK21 开发者,我建议你:

  1. 把书里的并发章节读透

  2. 理解 Monitor、JMM、CAS

  3. 再去学:

    • 虚拟线程

    • Structured Concurrency

    • ScopedValue

    • pin 问题

    • Carrier 线程调度

这样你会有一种感觉:

原来 JVM 的底层没有变,只是并发抽象升级了。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐