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);
}
这里是直接操作内存地址写入数据
 

 

posted @ 2020-10-16 14:41  郭星  阅读(197)  评论(0)    收藏  举报