为什么DCL单例需要volatile

单例设计模式

单例模式属于创建型设计模式,单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点

单例特性:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

常见实现方式

  1. 饿汉式
    不管是否使用,都直接创建对象。
public class Single01 {

    private static Single01 INSTANCE = new Single01();

    private static Single01 getInstance(){
        return INSTANCE;
    }
}
//另一种形式,和上面一个意思
class Single00 {

    private static Single00 INSTANCE;
    static{
        INSTANCE = new Single00();
    }

    private static Single00 getInstance(){
        return INSTANCE;
    }
}
  1. 懒汉式
    等到使用的时候才创建对象。
    问题:线程不安全
    原因:如果一个线程判断为null后挂起,另一个线程也进来判断为null,这时就两个线程都会创建INSTANCE
public class Single02 {
    private static Single02 INSTANCE;

    private static Single02 getInstance(){
        if(INSTANCE == null){
           INSTANCE = new Single02();
        }
        return INSTANCE;
    }
}
  1. 饿汉式–线程安全–粗粒度锁
    缺点:锁粒度太粗,效率低
public class Single03 {
    private static Single03 INSTANCE;

    private static synchronized Single03 getInstance(){
        if(INSTANCE == null){
            INSTANCE = new Single03();
        }
        return INSTANCE;
    }
}
  1. 饿汉式–细粒度锁
    问题:线程不安全
    原因:如果第一线程判断为null,进入if语句,但是没有上锁时被挂起,这时,第二个线程来了判断依然为null,然后上锁进入同步代码块创建实例,执行结束后释放锁,然后前一个线程拿锁继续执行,INSTANCE又创建一次
public class Single04 {
    private static Single04 INSTANCE;

    private static  Single04 getInstance(){
        if(INSTANCE == null){
            synchronized(Single04.class){
                INSTANCE = new Single04();
            }
        }
        return INSTANCE;
    }
}
  1. DCL(Double Check Lock)单例
    通过双重判断来解决细粒度锁方式出现的问题
public class Single05 {
    private static volatile Single05 INSTANCE;

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

DCL单例中volatile的必要性

首先需要知道,java对象的创建步骤:

  1. 分配对象空间,成员变量赋为类型默认值
  2. 调用构造方法对成员变量初始化
  3. 将对象空间地址值赋给字面量

其次,需要了解volatile的作用:

  1. 保证可见性
  2. 禁止指令重排

如果不加volatile,那么在创建对象过程很可能发生以下结果:

  1. 线程1分配了对象空间,并赋为成员变量赋了类型默认值
  2. 因为指令重排,初始化和地址赋值两步被颠倒,所以先赋了地址而没有初始化
  3. 此时,线程2进入,判断发现对象不为null,那么就会拿着这个没有初始化的对象直接去使用,从而导致程序出错

所以volatile在DCL单例中是非常重要的,不可或缺!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值