volatile的实现原理与应用

volatile关键字可以说是Java虚拟机提供的最轻量级的同步机制。当一个变量被声明为volatile之后,它具备三种特性。

  • 保证可见性:对共享变量的修改,其他线程可以立即感知到。
  • 保证有序性:编译器和runtime会禁止重排序
  • 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性

volatile的实现原理

Java代码如下

 instance = new DCLSingleton(); // instance是volatile变量

转成汇编代码如下
汇编代码
有volatile关键字修饰的共享变量进行写操作的时候会在指令前插入Lock前缀,该指令会在多核处理器下引入两件事情:

  • 将当前处理器缓存行的数据写回到系统内存
    Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号,如果访问的内存区域已经缓存在处理器内部,则会锁定这块区域的缓存并写会到内存,并使用缓存一致性机制来确保修改的原子性。
  • 这个写会内存的操作会使其他CPU里缓存了该内存地址的数据无效
    处理器使用MESI控制协议去维护内部缓存和其他处理器缓存的一致性。

volatile的内存语义

从JSR-133开始(即从JDK5开始),volatile变量的写-读可以实现线程间的通信。从内存语义的角度来说,volatile的写-读与锁的释放-获取有相同的内存效果。

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

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

volatile内存语义的实现

JMM针对编译器制定的volatile重排规则表:
volatile重排序规则表
从上表我们可以看出:

  • 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。
  • 当第一操作是volatile读时,不管第二个操作是什么,都不能重排序。
  • 当第一个操作时volatile写,第二个操作是volatile读时,不能重排序。

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

volatile的应用场景

状态标识

public class ShutdownDemo {
    private volatile boolean shutDownFlag = false;
   
    public void doWork() {
        while (!shutDownFlag) {
            // do work
        }
    }

    public void shutdown() {
        shutDownFlag = true;
    }
}

双重检查锁定

public class DCLSingleton {
    private static volatile DCLSingleton instance = null;
    private DCLSingleton() {
    }

    public static DCLSingleton getInstance() {
        if (null == instance) {
            synchronized (DCLSingleton.class) {
                if (null == instance) {
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

需要利用顺序性
volatile通过在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

volatile与synchronized的区别

使用上的区别对原子性的保证对可见性的保证对有序性的保证其它
volatile修饰变量不保证原子性保证可见性保证有序性无线程阻塞
synchronized修饰方法和语句块保证原子性保证可见性可以保证有序性,但是重量级锁会退化到串行会引起线程阻塞

大多数场景中,volatile的总开销要比锁来的低,我们在volatile与锁(synchronized或java.util.concurrent包里面的锁)中选择的唯一性判断依据仅仅是volatile的语义能否满足使用场景的需求。

什么时候该使用volatile呢?

当且仅当满足下列所有条件时,才应该使用volatile变量:

  • 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值
  • 该变量不会与其他状态变量一起纳入不变性条件中
  • 在访问变量时不需要加锁

参考资料
Java并发编程的艺术 方腾飞 魏鹏 程晓明 著
Java并发编程实战 童云兰 译


------------本文结束感谢您的阅读------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值