单例模式——Double Check 双重校验锁实现方式为什么要加volatile关键字

单例模式——Double Check 双重校验锁实现方式为什么要加volatile关键字

单例模式双重校验锁实例:

public class Singleton {

    private static volatile Singleton singleton = null;      //声明singleton时必须要加volatile关键字

    private Singleton() {}
    
    public static Singleton getInstance(){
        //第一次校验singleton是否为空
        if(singleton==null){                                 //1
            synchronized (Singleton.class){                  //2
                //第二次校验singleton是否为空                  
                if(singleton==null){                         //3
                    singleton = new Singleton();             //4
                }
            }
        }
        return singleton;                                    //5
    }

为什么声明singleton时,需要使用volatile关键字?

第一个作用是:

因为 singleton = new Singleton() 这句话可以分为三步:

  1. 为 singleton 分配内存空间;
  2. 初始化 singleton;
  3. 将 singleton 指向分配的内存空间。

但是由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。 指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程Thread1先执行了1、3,此时singleton已经指向了分配的内存空间,所以不为null,若这时有Thread2调用 getInstance() 在 //1 处发现 singleton 不为空,因此直接跳到 //5 处 return singleton, 但是此时Thread1并未对 singleton 进行初始化,那么返回的singleton是未被完整创建的singleton。而使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。

第二个作用是:

volatile关键字保证了变量的可见性,被volatile关键字修饰的变量,(1)在当前线程的工作内存中修改之后会立即更新回主内存;(2)在其他线程从自己的工作内存中读取该变量值之前,会先从主内存中更新最新的值到各自的工作内存。因此当 进入synchronized同步代码块的线程成功创建了实例,并释放了锁之后,等待在 //2 处的的某个线程拿到锁进入同步代码块,并进行第二次if判断时,读取到的singleton已经成功创建实例,就不会再创建第二个实例,而是直接到 //5 返回创建好的singleton。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值