volatile的作用

前提知识:并发编程三个特性——原子性、可见性、一致性
参考并发编程(原子性、原子性、可见性、一致性)

原子性:锁,synchronized保证
可见性:volatile保证
有序性:多方面共同保障,如下
1、synchronized
2、lock
3、volatile(一定程度的有序性)
synchronized和lock在同一时刻只能让一个线程执行同步方法,当然就是有序的了。
volatile的有序性是指写入修改后的volatile变量这个操作必定优先于 读取这个变量。

volatile的作用

(1)保证并发编程的可见性(不保证原子性)

  • 单线程下修改读取数据时Java内存模型没问题;但是多线程下可能读取到脏数据,需要用volatile修饰
  • 用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值
  • volatile可见性是通过汇编加上Lock前缀指令,触发底层的MESI缓存一致性协议来实现的

(2)禁止指令重排序

  • 指令的执行顺序并不一定会像我们编写的顺序那样执行,为了保证执行上的效率,JVM(包括CPU)可能会对指令进行重排序。单线程时重排序无影响,但是多线程时顺序错了,可能就不符合需求了,需要保证执行顺序的变量可以用volatile修饰。
  • 典型例子:单例模式的懒汉式双重检测加锁实现方式
//懒汉式
public class Singleton{
    //私有化构造函数
    private Singleton(){}
    //先不创建对象,等用到的时候再创建
    private static volatile Singleton ins=null;  //避免重排序,volatile有内存屏障的功能
    //调用到这个方法了,证明是要被用到的了
    public static Singleton getInstance(){
        if(ins==null){                         //这个判断主要为了提高性能
            synchronized(Singleton.class){     //使用synchronized将锁的范围缩小,提高性能
                if(ins==null){                 //再判断一次是否为null(主要的判断)
                    ins=new Singleton();
                }
            }
        }
        return ins;
    } 
}

ins变量被volatile关键字所修饰,但是如果去掉该关键字,就不能保证该代码执行的正确性。这是因为“ins= new Singleton();”这行代码并不是原子操作,其在JVM中被分为如下三个阶段执行:
1)为ins分配内存
2)初始化ins
3)将ins变量指向分配的内存空间
由于JVM可能存在重排序,上述的二三步骤没有依赖的关系,可能会出现先执行第三步,后执行第二步的情况。也就是说可能会出现ins变量还没初始化完成,其他线程就已经判断了该变量值不为null,结果返回了一个没有初始化完成的半成品的情况。而加上volatile关键字修饰后,可以保证ins变量的操作不会被JVM所重排序,每个线程都是按照上述一二三的步骤顺序的执行,这样就不会出现问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值