程序次序规则和管程锁定规则定义了操作间的happens-before关系:前者保证单线程内源码顺序的逻辑因果,后者确保同一锁下解锁与后续加锁间的跨线程可见性,二者共同支撑jmm的可推理并发语义。

程序次序规则和管程锁定规则,不是强制编译器或CPU“必须按代码顺序执行”,而是定义了哪些操作之间存在可见性与执行顺序的保证关系。它们的约束力体现在:只要代码满足这些规则的条件,JMM 就确保前一个操作的结果对后一个操作可见,且不会因重排序破坏这种逻辑因果。
程序次序规则:单线程内的“逻辑先后”是底线
它规定:在一个线程内,按源码书写顺序,前面的操作 happens-before 后面的操作。这不是说指令不能重排,而是说重排后的结果必须等价于按代码顺序执行的效果。
- 比如
a = 1; b = a + 1;,即使 JVM 把b = a + 1提前执行,也必须保证a已被赋值为 1,否则就违反了该规则——因为b的计算依赖a的值。 - 这个规则是单线程安全的基础。没有它,连最简单的赋值链都会不可预测。
- 它不跨线程起作用,仅保证本线程内操作间的偏序,但它是其他规则(如锁、volatile)能生效的前提。
管程锁定规则:解锁与加锁构成跨线程的“可见性桥梁”
它规定:对同一把锁,一个线程的 unlock 操作 happens-before 另一个线程后续对该锁的 lock 操作。关键在于“同一把锁”和“后续”——这里的“后续”指实际运行时的时间顺序,不是代码位置。
- 这意味着:线程 A 在 synchronized 块内修改了共享变量并退出,其所有写操作结果,对紧接着获取同一把锁的线程 B 是可见的。
- 它不保证“谁先抢到锁”,只保证一旦 B 成功加锁,就一定能看到 A 解锁前写入的所有内容——这是 JMM 插入内存屏障实现的,不是靠锁本身“传递数据”,而是靠同步机制强制刷新缓存。
- 如果两个线程用的是不同锁对象,或者 B 加锁发生在 A 解锁之前(时间上),这条规则就不适用,也就没有可见性保证。
两者共同作用:构建可推理的并发行为
程序次序规则划定线程内部的因果链,管程锁定规则在锁交接点打通线程间的数据流。它们合起来,让开发者能基于代码结构判断“某处修改是否一定被另一处读到”。
- 例如:线程 A 先写
flag = true,再unlock;线程 B 先lock,再读flag。若两段代码用同一把锁,则 B 读到的一定是true——因为 A 的写操作 happens-before A 的 unlock,A 的 unlock happens-before B 的 lock,B 的 lock happens-before B 的读,再由传递性得出 A 的写 happens-before B 的读。 - 这种推理不依赖具体执行时间或调度顺序,只依赖代码结构和同步动作,正是 happens-before 的价值所在。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











