Volatile自赋值(fld=fld)的适用场景及与内存屏障的等价性问询
Volatile自赋值
fld = fld的适用场景与替代方案 好问题!这种看似无意义的fld = fld写法其实是Java并发编程里的一种历史遗留的权宜之计,用来模拟内存屏障的效果,它曾经有特定的适用场景,但如今已经被更规范、高效的方案替代了。
为什么fld = fld能起到内存屏障的作用?
因为fld是volatile变量,根据JMM(Java内存模型)的规则:
- Volatile变量的读操作会触发
LoadLoad和LoadStore屏障,阻止读操作与后续读写操作重排序; - Volatile变量的写操作会触发
StoreStore和StoreLoad屏障,阻止前面的读写操作与写操作重排序。
而fld = fld的执行逻辑是:先读取这个volatile变量,再写入它。这两个操作组合起来,就相当于一个全内存屏障(full fence)——任何跨越这条语句的加载、存储操作都无法重排序,和你提到的U.fullFence()效果在语义上完全一致。
它曾经的适用场景
在Java 5之前,Java标准库没有提供公开的内存屏障API,Unsafe类虽然存在但属于内部未公开的API,大多数开发者不敢轻易使用。这时候,开发者会用这种volatile自赋值的技巧,在自定义并发组件中实现内存可见性和禁止指令重排序的需求:
- 确保某个代码块的操作对其他线程可见后,再执行后续逻辑;
- 避免编译器或CPU对前后代码进行重排序,破坏并发逻辑的正确性。
为什么现在不推荐使用?
和U.fullFence()(或现代Java中的VarHandle.fullFence())相比,这种写法有明显的劣势:
- 可读性极差:
fld = fld看起来完全像笔误,维护代码的人很容易误以为是无用代码而删除,或者无法理解它的真实意图; - 性能略逊:虽然语义等价,但
fld = fld涉及两次volatile内存操作(读+写),会带来额外的缓存一致性开销,而fullFence()是直接触发底层的内存屏障指令,更高效; - 有更规范的替代方案:Java 9之后引入的
VarHandle类提供了安全、公开的内存屏障方法(fullFence()、acquireFence()等),完全可以替代这种hack式的写法。
总结
fld = fld这种无意义写入并不是没有适用场景,它是早期Java并发工具不完善时的权宜之计。但在现代Java版本中,它已经属于过时的技巧,建议优先使用VarHandle提供的内存屏障方法,或者利用Java标准并发库(如java.util.concurrent下的工具类)来实现对应的并发需求。
内容的提问来源于stack exchange,提问作者Some Name
相关产品推荐
相关产品推荐

