仅限会员阅读

单例模式的双检锁实现方式中,如果不使用volatile关键字,为什么会出现空指针问题?

2024年05月20日 12:14  ·  阅读 434

   单例模式中的双检锁(Double-Checked Locking, DCL)是一种用于延迟初始化单例实例的线程安全的方法,其主要思想是在没有竞态条件(race condition)的情况下,避免使用锁,以提高性能。它解决了单例、性能、线程安全问题。

1、代码示例

public class Singleton {  
    // 在成员位置创建该类的对象
    private static volatile Singleton instance;  
    
    // 私有构造方法
    private Singleton (){}  
    
    // 对外提供静态方法获取该对象
    public static synchronized Singleton getInstance() {  
        // 如果instance不为null,不进入抢锁阶段,直接返回实例
        if (instance == null) {  
            synchronized (Singleton.class) {
                // 抢到锁之后再次判断是否为null
                if(instance == null) {
                    singleton = new Singleton();
                }       
            }
        }  
        return instance;  
    }  
}

注意:在上面的代码中,instance 字段被声明为 volatile。这是因为在没有 volatile 修饰符的情况下,原始的DCL实现可能会出现空指针问题。

2、为什么可能出现空指针的问题?

在Java中,对象的创建过程不是原子的。当执行 new Singleton() 时,实际上会分为三个步骤:

  1. 分配内存空间给对象。
  2. 初始化对象(即设置对象的各个字段)。
  3. 将对象的引用(即内存地址)赋值给变量 instance

由于Java内存模型允许指令重排(Instruction Reordering),编译器和处理器可能会对上述步骤进行重排以提高性能。这可能导致在没有 volatile 修饰符的情况下,其他线程看到 instance 的引用已经被赋值(步骤3),但对象本身可能还没有完全初始化(步骤2尚未完成)。

如果另一个线程在这个时间点访问 instance,它可能会看到一个非空的 instance 引用,但尝试访问对象的字段或方法时可能会遇到空指针异常(因为对象本身可能还没有完全初始化)。

专栏家上阅读行业精英撰写的最佳文章

作者仅向会员开放了此篇文章。开通会员即可立即解锁此文章和其他会员专属权益。

所有会员专属文章免费阅读
成为你感兴趣领域的专家
获取关于技术的数千个问题的深入解答
发展你的职业生涯或开始新的职业
评论
全部评论