volatile 是 Java 中的关键字,是一个变量修饰符,被用来修饰会被不同线程访问和修改的变量
一旦一个共享变量被 volatile 修饰之后,就具备两层语义
ACC_VOLATILE
为了实现 volatile 的内存语义,JMM会限制特定类型的编译器和处理器重排序,JMM会针对编译器制定 volatile 重排序规则表,“NO”表示禁止重排序。
编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
StoreStore
屏障,后面插入StoreLoad
屏障LoadLoad
和 LoadStore
屏障int field_offset = cache->f2_as_index();
if (cache->is_volatile()) {
if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
OrderAccess::fence();
}
}
orderaccess_linux_x86.inline.hpp
inline void OrderAccess::fence() {
// always use locked addl since mfence is sometimes expensive
#ifdef AMD64
__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
compiler_barrier();
}
如果硬件架构本身已经保证了内存可见性(单核处理器、一致性足够的内存模型),或者硬件架构本身不进行处理器重排序,或者有更强的重排序语义(能够分析多核间的数据依赖),那么volatile就是一个空标记,不会插入相关语义的内存屏障。
查看源代码,可以看到在汇编层面,带有 volatile 修饰的变量前面会有lock add1 $0x0, (%rsp)
这么一串指令,由此可见 volatile 是通过LOCK指令前缀来达到内存屏障的作用。