volatile实现细节:
字节码层面:
- 字段的访问标志增加了一个0x0040 [volatile]
JVM层面:前后都会加屏障,不能互换
- StoreStoreBarrier
- volatile 写操作
- StoreLoadBarrier
- LoadLoadBarrier
- volatile读操作
- LoadStoreBarrier
hotspot的实现
偷懒了,并没有根据不同的CPU做优化,而是使用的所有CPU都通用的lock指令
bytecodeinterpreter.cpp
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() {
if (os::is_MP()) {
// 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
}
}
- lock用于在多处理器中执行指令时对共享内存的独占使用,它的作用是能将当前处理器对应缓存的内容刷新到内存,并使其他处理器对应的缓存失效,另外还提供了有序的指令无法越过这个内存屏障的作用。
- lock; addl $0,0(%%esp)相当于一个内存屏障(Memory Barrier,指重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个cpu核访问内存时,并不需要内存屏障,但如果有两个或者更多CPU核访问同一块内存,且其中有一个在观测另一个,就需要内存屏障来保证一致性,addl $0,0(%%esp)是一条空操作,可以让前面的volatile变量的修改对其他CPU立即可见。
volatile并不能保证原子性
在volatile修改之后,增加了一个内存屏障lock; addl $0,0(%%esp),内存屏障之前并不是原子的