volatile和DCL单例

本文详细解析了在双重检查锁定(DCL)单例模式中使用volatile关键字的原因。通过解释重排序的概念及volatile如何阻止处理器重排序,确保了单例对象的正确初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间的面试里面,被问到volatile为什么要用到双检锁(Double Check Lock)单例中?没答上-.-||后来google了下,这个问题的回答还是很多的。我们都知道,volatile的作用:

用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致 的情况。volatile就是用来避免这种情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

但是,DCL单例模式中为什么要用volatile修饰单例变量呢?下面是一段DCL单例的代码:

public class Singleton{

 private int data = 1;//初始化变量

 private static volatile singleton = new Singleton();

 private Singleton(){
 }

 public static Singleton getInstance(){
  
  if(singleton == null){
   
   synchronized(this){

    if(singleton == null){
     singleton = new Singleton();
    }
   }
  }
  return singleton;
 }
}

这里的问题是问volatile的作用是怎么在singleton = new Singleton();中体现的。这里涉及到“重排序”。

什么是重排序?

比如,两个指令,(1)int a = 1;(2)int b = 2;如果(1)和(2)之间没有数据依赖,那么编译器和处理器就会对这两个指令进行重排序,也就是可能(2)会比(1)先执行。

回到DCL单例模式,在语句singleton = new Singleton() 中,实际上是分为三个操作:(1)分配singleton对象的内存(2)初始化singleton对象(3)把singleton引用指向singleton对象

现在(2)(3)是没有数据依赖的,那处理器可能会先把singleton引用指向singleton对象,然后在初始化singleton对象。如果这样,在线程1执行完(3)后,线程2判断if(singleton == null)时就会是false,然后返回singleton实例,此时这个实例没有初始化,线程2得不到data的值是1。所以为了避免这个问题,volatile在三步操作中都加了内存屏障这种东西,屏蔽掉处理器的重排序。

转载于:https://my.oschina.net/ChiLin/blog/754320

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值