关于JMM中volatile顺序一致性及happens-before关系的疑问
JMM规范相关疑问解答
1. 此示例中volatile是否真能保证顺序一致性结果?原因是什么?
是的,将A和B声明为volatile后,确实不会出现r2=0且r1=0的结果,只会产生符合顺序一致性的组合(比如(0,1)、(2,0)、(2,1),对应你提到的[0,1]、[1,0]、[1,1]是表述简化),核心原因在于volatile的内存屏障机制和重排序禁止规则:
重排序禁止:volatile写操作会插入
StoreLoad内存屏障,该屏障会禁止后续的读操作重排到当前写操作之前。具体到示例:- 线程1中,
B=1(volatile写)之后的r2=A(volatile读)无法被重排到B=1之前,线程1必然先完成B的写入,再去读A; - 线程2中,
A=2(volatile写)之后的r1=B(volatile读)同样无法被重排到A=2之前,线程2必然先完成A的写入,再去读B。
这直接排除了“线程1先读A再写B、线程2先读B再写A”这种导致双0结果的重排场景。
- 线程1中,
可见性保证:volatile写操作会立即将变量值刷新到主内存,volatile读操作会直接从主内存加载变量值。这意味着:
- 线程1写完B=1后,线程2后续读B时一定能看到这个值;
- 线程2写完A=2后,线程1后续读A时一定能看到这个值。
综上,volatile通过禁止关键重排和强制内存刷新/加载,确保了不会出现违背顺序一致性的双0结果。
2. 同一线程中x在hb顺序中先于y是如何成立的?
这是JMM定义的程序顺序规则,是happens-before(hb)关系的基础规则之一,和“编译器/CPU允许重排单线程内操作”并不冲突:
- JMM允许单线程内的指令重排,但必须遵守as-if-serial语义——重排后的执行结果必须和按程序顺序执行的结果完全一致。单线程内的hb(x,y)关系,就是JMM对这种语义的形式化定义:它保证了同一线程内,先发生的操作x的结果对后发生的操作y可见,且y无法感知到x被重排到自己之后(因为重排不能改变单线程的执行结果)。
举个例子:
- 如果线程内有
a=1; b=a;,编译器无法重排这两个操作(因为b依赖a的结果),hb(a=1, b=a)显然成立; - 如果线程内有
a=1; b=2;,编译器可以重排,但hb(a=1, b=2)依然成立——从逻辑上看,b=2的执行“依赖”a=1完成(即使重排,单线程内也不会出现b=2执行时a还未赋值的情况),JMM通过hb关系确保这种逻辑上的先后顺序。
另外,跨线程的hb规则(比如volatile写读、锁的解锁加锁)都是基于单线程hb规则延伸的——JMM通过组合单线程hb和跨线程hb,来定义整个程序的内存可见性约束。
内容的提问来源于stack exchange,提问作者Роман Григорьев
相关产品推荐
相关产品推荐

