JCStress测试中volatile未消除竞态、static反而消除竞态的原因是什么?
核心原因说明
1. volatile场景的多结果是合法预期,不存在失效问题
你实现的是典型的IRIW(独立写的独立读)测试用例,观测到的多种结果完全符合Java内存模型(JMM)的规范,并非volatile没有生效。volatile的语义仅做了以下保证:
- 对单个
volatile变量的写操作,Happens-Before于后续所有对该变量的读操作 - 针对同一个
volatile变量的读写不会被指令重排序打乱
但JMM没有要求不同线程对两个独立volatile变量的写操作有全局一致的观测顺序。你启动测试时添加了-XX:+StressLCM、-XX:+StressGCM等JIT应力测试参数,JIT会故意调整指令执行顺序来模拟弱内存架构下的重排序行为,出现你列出的所有观测结果都是预期内的正常情况。
2. static非volatile场景的全1结果是测试用例编写错误导致的假象
JCStress的默认运行逻辑为:每次迭代测试时,都会创建一个新的被@State注解的类实例,实例字段会被自动初始化为默认值0,保证每次测试的初始状态一致。
当你把x、y改为static修饰后,这两个变量属于类层面的全局共享变量,不会随着@State实例的重新创建而重置:第一次测试迭代中两个写Actor执行后,x和y就被永久赋值为1,后续所有测试迭代读取这两个静态变量拿到的永远是1,自然只会出现1,1,1,1的结果。这是测试用例不符合JCStress编写规范导致的错误结果,和static修复竞态没有任何关系,也不是JCStress本身的问题。
如果需要测试静态非volatile变量的行为,需要手动添加@Setup注解的方法,在每次迭代前主动重置静态变量,同时要注意规避重置操作本身引入的同步干扰。通常来说@State类不推荐使用静态字段,使用实例字段才是符合JCStress设计的正确写法。
内容的提问来源于stack exchange,提问作者ExtraExtremeERA
相关产品推荐
相关产品推荐

