You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅标记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 1Thread 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_B42 HB于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的条件:

  1. 不存在hb(r, w)(即r不排在w之前);
  2. 不存在其他写入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 22:50:00