BiBi -2- 并发编程 - volatile

2018-12-23  本文已影响12人  奋飞的蜗牛ing

From:Java并发编程的艺术

2、volatile

class VolatileExample {
  int a = 0;
  volatile boolean flag = false;
  public void writer(){
    a = 1; // 1
    flag = true; // 2
  }
  public void reader(){
    if (flag){ // 3
      int i = a; // 4
    }
  }
}

线程A执行writer()方法之后,线程B执行reader()方法。根据happens-before规则,操作1 happens-before 操作4,因此可以正常运行。【volatile的写-读与锁的释放-获取具有相同的内存效果】【在线程B读一个volatile变量后,线程A在写这个volatile变量之前所有可见的共享变量的值都将立刻变得对线程B可见】【即操作2 执行完成后,会把共享变量 a 和 flag 刷新到主内存】

1)线程A写一个volatile变量,实质上是线程A向接下来将要读这个volatile变量的某个线程发出【对共享变量所做的修改的】消息。如:上述中的 a = 1。
2)线程B读一个volatile变量,实质上是线程B接收了之前某个线程发出的【在写这个volatile变量之对共享变量所做修改的】消息。如:上述中的 a = 1。

根据volatile重排序规则可知:操作 1 和操作 2 不会重排序;操作 3 和操作 4 不会重排序。

为了实现volatile内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

  1. 在每个volatile写操作的面插入一个StoreStore屏障。
    StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存。
  2. 在每个volatile写操作的面插入一个StoreLoad屏障。
    StoreLoad避免volatile写与后面可能有的volatile读/写操作重排序。StoreLoad可以在每个volatile写后面,也可以在每个volatile读前面进行插入,但从整体效率角度考虑,常见的使用模式:一个线程写volatile变量,多个线程读volatile变量。所以JVM最终选择的是在每个volatile写后面插入StoreLoad屏障,
  3. 在每个volatile读操作的面插入一个LoadLoad屏障。
    LoadLoad禁止下面所有的普通读操作和上面的volatile读重排序。
  4. 在每个volatile读操作的面插入一个LoadStore屏障。
    LoadStore禁止下面所有的普通写操作和上面的volatile读重排序。
上一篇 下一篇

猜你喜欢

热点阅读