java学习-JMM-volatile
volatile 实际是针对单一变量的加锁同步操作; 对于volatile的同步操作是利用的 memeory-barrier 实现的;
https://en.wikipedia.org/wiki/Memory_barrier
对于java 中的底层实现可以查看 openjdk中的orderAccess.cpp文件
volatile的另一层语义为可见性,对于多核cpu下的多线程操作,对于线程中的数据会被加载到当前线程所在的cpu的寄存器中,而对于存在于jvm中的数据是存在在内存中的,对于内存而言就是cpu的主存;
但对于多个线程在不同cpu核心上进行修改volatile修饰的共享变量时,在openjdk源码中会利用汇编直接修改内存中的数据并使其他cpu核心寄存器中的共享变量副本失效,从而保证其他线程再次读取时会从内存中重新加载到寄存器中;
从而实现了其他线程的可见性
对于被volatile 修饰的变量在编译为class文件后 会有 "ACC_VOLATILE" 标识
public class VolatileDemo { public volatile int i; // 对于 volatile 实际是利用了 c++ 中的 orderAccess 中的内存屏障实现了可见性 }
使用 javap -v VolatileDemo
public volatile int i; descriptor: I flags: (0x0041) ACC_PUBLIC, ACC_VOLATILE
可以看到对变量i 而言,在编译为字节码后 拥有"ACC_VOLATILE " 标识;
对于 "ACC_VOLATILE" 在 c++中的使用,在ciFlags.hpp中 存在一下代码:
bool is_volatile () const { return (_flags & JVM_ACC_VOLATILE ) != 0; }
可以看到对应的方法实际就是 "is_volatile",
在 src/hotspot/share/interpreter/bytecodeInterpreter.cpp 中存在 一下代码
// // Now store the result on the stack // TosState tos_type = cache->flag_state(); int field_offset = cache->f2_as_index(); if (cache->is_volatile()) { // 该处的整体操作是获取数据,直接通过内存中的相对地址直接获取内存中的地址,并将其加载到当前线程的栈中,因此 才保证了可见性 if (support_IRIW_for_not_multiple_copy_atomic_cpu) { OrderAccess::fence(); // 开启内存屏障 } }
//
// Now store the result 这里的操作时对volatile 变量进行赋值的操作
//
int field_offset = cache->f2_as_index();
if (cache->is_volatile()) {
if (tos_type == itos) {
obj->release_int_field_put(field_offset, STACK_INT(-1));
在 oop.cpp中存在
void oopDesc::release_int_field_put(int offset, jint value) { HeapAccess<MO_RELEASE>::store_at(as_oop(), offset, value); }
static inline void store(void* addr, T value) {
store_internal<decorators>(addr, value);
}
这里是直接操作内存地址写入数据

浙公网安备 33010602011771号