吃透面试一:DCL单例为什么要加volatile

一、什么是DCL

DCL的全拼是double check lock,也就是双重锁判断机制。

二、回顾几种单例

1、饿汉式

public class SingleInstance {
    private SingleInstance(){}
    private static final SingleInstance INSTANCE=new SingleInstance();
    public static SingleInstance getInstance(){
        return INSTANCE;
    }
}

优点:线程安全、调用效率高
缺点:不能延时加载

2、懒汉式

public class SingleInstance {
    private SingleInstance(){}
    private static SingleInstance INSTANCE;
    public static synchronized SingleInstance getInstance(){
        if (INSTANCE==null){
            INSTANCE=new SingleInstance();
        }
        return INSTANCE;
    }
}

优点:线程安全、能延时加载。
缺点:锁的粒度太大,导致调用效率不高。

3、DCL

public class SingleInstance {
    private SingleInstance() {}
    private static volatile SingleInstance INSTANCE;
    public static SingleInstance getInstance() {
        if (INSTANCE == null) {
            synchronized (SingleInstance.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SingleInstance();
                }
            }
        }
        return INSTANCE;
    }
}

优点:线程安全、调用效率高、能延迟加载。

4、匿名内部类

public class SingleInstance {
    private SingleInstance() {}
    private static final class HolderClass{
        private static final SingleInstance INSTANCE=new SingleInstance();
    }
    public static SingleInstance getInstance() {
        return HolderClass.INSTANCE;
    }
}

优点:线程安全,调用效率高,可以延时加载。

三、DDL单例为什么要加volatile

我们把上面写的DDL单例拿过来,加入不加volatile,如下:

public class SingleInstance {
    private SingleInstance() {}
    private static SingleInstance INSTANCE;
    public static SingleInstance getInstance() {
        if (INSTANCE == null) {
            synchronized (SingleInstance.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SingleInstance();
                }
            }
        }
        return INSTANCE;
    }
}

当 INSTANCE = new SingleInstance() 创建实例对象时,并不是原子操作,它是分三步来完成的:

  1. 创建内存空间。
  2. 执行构造函数,初始化(init)
  3. 将INSTANCE引用指向分配的内存空间

上述正常步骤按照1–>2–>3来执行的,但是,我们知道,JVM为了优化指令,提高程序运行效率,允许指令重排序。正是有了指令重排序的存在,那么就有可能按照1–>3–>2步骤来执行,这时候,当线程a执行步骤3完毕,在执行步骤2之前,被切换到线程b上,这时候instance判断为非空,此时线程b直接来到return instance语句,拿走instance然后使用,接着就顺理成章地报错(对象尚未初始化)。

synchronized虽然保证了线程的原子性(即synchronized块中的语句要么全部执行,要么一条也不执行),但单条语句编译后形成的指令并不是一个原子操作(即可能该条语句的部分指令未得到执行,就被切换到另一个线程了)。

volatile关键字其中一个作用就是禁止指令重排序,所以DCL单例必须要加volatile

volatile作用:

  1. 保证被修饰的变量对所有线程的可见性。
  2. 禁止指令重排序优化。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃透Java

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值