单例模式中的双检锁(Double-Checked Locking, DCL)是一种用于延迟初始化单例实例的线程安全的方法,其主要思想是在没有竞态条件(race condition)的情况下,避免使用锁,以提高性能。它解决了单例、性能、线程安全问题。
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实现可能会出现空指针问题。
在Java中,对象的创建过程不是原子的。当执行 new Singleton()
时,实际上会分为三个步骤:
instance
。由于Java内存模型允许指令重排(Instruction Reordering),编译器和处理器可能会对上述步骤进行重排以提高性能。这可能导致在没有 volatile
修饰符的情况下,其他线程看到 instance
的引用已经被赋值(步骤3),但对象本身可能还没有完全初始化(步骤2尚未完成)。
如果另一个线程在这个时间点访问 instance
,它可能会看到一个非空的 instance
引用,但尝试访问对象的字段或方法时可能会遇到空指针异常(因为对象本身可能还没有完全初始化)。
作者仅向会员开放了此篇文章。开通会员即可立即解锁此文章和其他会员专属权益。