Java并发编程图册学习(六) 面试 volatile 关键字

对一个 volatile 域的写, happens-before 任意后续对这个 volatile 域的读

一、happens-before 之 volatile 变量规则

JMM 针对编译器定制的 volatile 重排序的规则,那 JMM 是怎样禁止重排序的呢? 答案是内存屏障 

二、内存屏障 (Memory Barriers / Fences)

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

这句话有点抽象,试着想象内存屏障是一面高墙如果两个变量之间有这个屏障, 那么他们就不能互换位置(重排序)了,变量有读(Load)有写(Store),操作有前有后, JMM 就将内存屏障插入策略分为 4 种:

1. 在每个 volatile 操作的面插入一个 StoreStore 屏障

2. 在每个 volatile 操作的面插入一个 StoreLoad 屏障

3. 在每个 volatile 操作的面插入一个 LoadLoad 屏障

4. 在每个 volatile 操作的面插入一个 LoadStore 屏障

 

三、volatile 写-读的内存语义

 假定线程 A 先执行 writer 方法,随后线程 B 执行 reader 方法

public class ReorderExample {

    private int x = 0;
    private int y = 1;
    private volatile boolean flag = false;

    public void writer(){ 
        x = 42; //1
        y = 50; //2
        flag = true; //3
    }

    public void reader(){ 
        if (flag){ //4
            System.out.println("x:" + x); //5
            System.out.println("y:" + y); //6 
        }
    } 
}

volatile 读的内存语义:

 

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

所以当线程 B 执行 reader 方法时,线程 B本地内存变量无效,从主内存中读取变量到本地内存中,也就得到了线程 A 更改后的结果,这就是 volatile 是如何保证可⻅性的

  1. 线程 A 写一个volatile变量, 实质上是线程 A 向接下来将要读这个 volatile 变量 的某个线程发出了(其对共享变量所做修改的)消息

  2. 线程 B 读一个 volatile 变量,实质上是线程 B 接收了之前某个线程发出的(在写 这个 volatile 变量之前对共享变量所做修改的)消息。

  3. 线程 A 写一个 volatile 变量, 随后线程 B 读这个 volatile 变量, 这个过程实质 上是线程 A 通过主内存向线程B 发送消息。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值