仅标记B为volatile时为何能读取B==0?基于JMM文档解析
仅标记B为volatile时读取到B==0的JMM规范分析
核心结论
当仅B被标记为volatile时,Thread 2的print(B)读取到B==0是完全符合JMM规范的,你的推理误区在于错误假设了线程内操作的执行顺序,以及对happens-before(HB)关系与读取可见性的绑定逻辑理解偏差。
场景回顾
初始状态A==B==0,线程执行逻辑:
| Thread 1 | Thread 2 |
|---|---|
B=42; | if (A==1) |
A=1; | ...print(B) |
已知:
- 标记
A为volatile时,print(B)仅能读到42; - 仅标记
B为volatile时,print(B)可能读到0或42。
关键误区拆解
你之前的分析默认Thread 2会严格按照代码顺序先执行if(A==1)(读取A),再执行print(B)(读取B),但JMM允许线程内的指令重排序——只要不违反程序顺序(PO)的HB关系,普通操作和volatile操作之间存在合法的重排序空间。
1. Thread 2的合法重排序
JMM的重排序规则明确:volatile读取之前的普通操作,可以被重排序到volatile读取之后。对于Thread 2:
if(A==1)是对普通变量A的读取(记为r_A);print(B)是对volatile变量B的读取(记为r_B)。
因此,Thread 2的实际执行顺序可能被重排为:
1. 读取B(r_B)→ 读到初始值0,暂存结果 2. 读取A(r_A)→ 读到Thread 1写入的1 3. 执行print(B) → 输出之前暂存的0
2. 结合JMM规范验证合法性
根据JMM规范,我们逐一验证这种执行的合规性:
(1)HB关系梳理
Thread 1内部:B=42(记为w_B42)HB于A=1(记为w_A1)(程序顺序规则);- 默认值写入:
A=0(w_A0)、B=0(w_B0)分别HB于两个线程的所有操作; - Volatile同步:仅当
w_B42在**同步顺序(SO)**中先于r_B时,w_B42HB于r_B;但在重排后的执行中,r_B发生在w_B42之前,因此hb(w_B42, r_B)不成立; w_A1与r_A之间:无HB关系(A是普通变量,无同步动作连接),因此r_A可以读到w_A1(JMM允许普通变量的可见性延迟被打破,只要不违反HB)。
(2)读取可见性条件验证
对于r_B读取w_B0的情况:
JMM规定读取
r可观察到写入w的条件:
- 不存在
hb(r, w)(即r不排在w之前);- 不存在其他写入
w'满足hb(w, w')且hb(w', r)。
- 条件1:
hb(w_B0, r_B)成立,因此不存在hb(r_B, w_B0),满足条件; - 条件2:唯一的其他写入是
w_B42,但hb(w_B42, r_B)不成立(r_B先于w_B42执行),因此不存在符合要求的w',满足条件。
3. 为什么标记A为volatile时不会出现这种情况?
当A被标记为volatile时:
w_A1(volatile写入)HB于r_A(volatile读取)(同步顺序规则);- 结合
Thread 1内部的hb(w_B42, w_A1),通过HB的传递性可得hb(w_B42, r_A); - 再结合
Thread 2内部的hb(r_A, r_B)(程序顺序),最终hb(w_B42, r_B)成立; - 根据JMM规则,若
hb(w, r),则r必须观察到w或后续写入,因此r_B只能读到w_B42(即42)。
内容的提问来源于stack exchange,提问作者l30c0d35
相关产品推荐
相关产品推荐

