Java内存模型-volatile内存语义

章节目录

  • 1.volatile 的特性
    • 为什么volatile修饰变量的写操作不是原子性的?
  • 2.volatile 写-读建立的 happens-before 关系
  • 3.volatile 写-读的内存语义

1.volatile 的特性

首先应该明确的一点是:当声明共享变量为volatile后,对这个变量的读/写(分为单元素读写,与复合写操作)。不同的读写模式下,volatile变量对写操作的原子性体现是不一样的。

理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是同一个锁对这些单个读/写操作做了同步,示例代码如下所示:

class VolatileFeaturesExample {
   volatile long vl = 0L;
   public void set(long l){
      vl = l;
   }

   public void getAndIncrement() {
       vl++;
   }

  public long get(){
     return vl;
  }
}

假设有多个线程调用上述程序中的3个方法,这个程序语义和下面的程序语义等价

class VolatileFeaturesExample {
   long vl = 0L;
   public  synchronized void set(long l){
      vl = l;
   }

   public void getAndIncrement() {
       long temp = get();
       temp +=1L;
       set(temp);
   }

  public syntronized long get(){
     return vl;
  }
}

解释

  • 如这两个程序所示,一个volatile 变量的单个读/写操作,与一个普通变量的读/写操作都是使用同一个锁来同步,他们之间的执行效果相同。
  • 锁的happens-before规则(保证前一个操作结果对后一个操作立即可见)保证释放锁和获取锁的两个线程之间的内存可见性。这很好的解释了对于一个volatile变量的读,总能看到任意线程对这个volatile变量最后的写入。
  • 锁的语义决定了临界代码区的执行具有原子性。这意味着,即使是64位的long型和double型变量,只要它是volatile变量,对该变量的单个读/写就具有原子性,如果是多个volatile操作,或这是volatile++这种复合操作,这些操作在整体上是不具有原子性的。

volatile 自身特性:

1.内存可见性:对一个volatile变量的读,总能看到任意线程对这个volatile变量
  最后的写入(内存可见性保证)
2.原子性:对任意单个volatile变量的单独的读写操作,都具有原子性。但对于
  volatile++这种复合操作不具有原子性。

2.volatile 写-读建立的 happens-before 关系

JMM基于共享内存模型实现线程之间的通信

从jdk1.5 开始 volatile 变量的写-读可以实现线程之间的通信。

volatile & synchronized内存语义

从内存语义来说,volatile的写-读与锁的释放-获取有相同的内存效果:volatile
写和锁释放有相同的内存语义;volatile读与锁的获取有相同的内存语义。

下面为volatile变量示例代码:

class VolatileExample {
    int a = 0;//其实是线程共享变量
    volatile boolean flag = false;

    public  void writer(){
        a = 1;                   //1
        flag = true;             //2
    }

   public void reader(){
       if(flage){                //3
         int i = a;              //4
         ......
      }
   }
}

假设线程A 执行 writer() 方法之后,线程B 执行reader() 方法。根据happens-before 规则,这个过程建立的happens-before规则可以分为三类:
1.根据程序次序规则,1 happens before 2;3 happens before 4.
2.根据volatile规则,2 happens-before 3
3.根据happens-before c传递性规则 1 happens-before 4

图形化形势如下图所示:

volatile-写-读 happens-before 关系

A线程在写一个volatile变量后,B线程读同一个volatile变量。A线程在写volatile之前所有的可见共享变量,在B线程读到同一个volatile变量之后,将立即变得对B线程可见。

4. volatile 写-读的内存语义

** volatile 写内存语义 **

当写一个volatile变量时,JMM会把该线程对应的本地内存的变量中的变量值强制刷新到主内存。

volatile 读的内存语义

 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

volatile内存语义 总结

  • 线程A 写一个volatile 变量,实质上是线程A 向接下来读取这个volatile变量的某个线程发出了(其对共享变量所做修改的)消息。
  • 线程B 读一个volatile 变量,实质上是线程B 接收了之前某个线程发出的(在写这个volatile变量之前对共享变量所做修改)消息。
  • 线程A 写一个volatile 变量,随后线程B读这个volatile变量。这个过程实质上是线程A通过主内存向线程B发送消息。

你可能感兴趣的:(Java内存模型-volatile内存语义)