JVM-volatile的内存语义

2018-06-04  本文已影响0人  Briarbear

本篇内容主要摘自《Java并发编程的艺术-方腾飞》


1. volatile特性

class VolatileFeaturesExample {
    volatile long vl = 0L; //使用volatile声明64位的long型变量
 
    public void set(long l) {
        vl = l; //单个volatile变量的写
    }
 
    public void getAndIncrement() {
        vl++; //复合(多个)volatile变量的读/写
    }
 
    public long get() {
        return vl; //单个volatile变量的读
    }
}
class VolatileFeaturesExample {
    long vl = 0L; // 64 位的 long 型普通变量
    public synchronized void set(long l) { // 对单个的普通变量的写用
        vl = l;
    }
 
    public void getAndIncrement () {// 普通方法调用
        long temp = get();// 调用已同步的读方法
        temp += 1L;// 普通写操作
        set(temp);// 调用已同步的写方法
    }
 
    public synchronized long get() { // 对单个的普通变量的读用同一个锁同步
        return vl;
    }
}

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

image

在上图中,每一个箭头链接的两个节点,代表了一个 happens-before 关系。黑色箭头表示程序顺序规则;橙色箭头表示 volatile 规则;蓝色箭头表示组合这些规则后提供的 happens-before保证

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


3. volatile 写 - 读的内存语义

volatile 写的内存语义如下

以上面示例程序 VolatileExample 为例,假设线程 A 首先执行 writer() 方法,随后线程 B执行 reader() 方法,初始时两个线程的本地内存中的 f lag 和 a 都是初始状态。图 3-17 是线程A 执行 volatile 写后,共享变量的状态示意图

image

如上图所示,线程 A 在写 f lag 变量后,本地内存 A 中被线程 A 更新过的两个共享变量的值被刷新到主内存中。此时,本地内存 A 和主内存中的共享变量的值是一致的

图 3-18 为线程 B 读同一个 volatile 变量后,共享变量的状态示意图。

如图所示,在读 f lag 变量后,本地内存 B 包含的值已经被置为无效。此时,线程 B 必须从主内存中读取共享变量。线程 B 的读取操作将导致本地内存 B 与主内存中的共享变量的值变成一致

如果我们把 volatile 写和 volatile 读两个步骤综合起来看的话,在读线程 B 读一个volatile 变量后,写线程 A 在写这个 volatile 变量之前所有可见的共享变量的值都将立即变得对读线程 B 可见

image
上一篇下一篇

猜你喜欢

热点阅读