设计模式 - 单例模式 - Tips

为什么双重检查会带来空指针异常 问题?

java 复制代码
if (instance == null) {             
    synchronized (Singleton.class) {                                 
        if (instance == null) {                     
            instance = new Singleton();                 
        }             
    }         
}

instance = new Singleton();

上述加粗的代码并不是原子操作,包含三个步骤:

  1. 为 Singleton 分配内存地址

  2. 执行 Singleton() 构造方法,初始化成员变量等

  3. 将内存地址赋值给 instance 变量

假设线程 A 正在执行:

java 复制代码
instance = new Singleton();   

由于指令 **重排序,**线程 A 可能在构造函数执行之前,就已经将内存地址赋值给 instance,导致 instance 变为非 null。此时线程 B 会认为 instance 已经是合法对象,于是直接使用,结果可能:

  • 访问尚未初始化的字段

  • 抛出 NullPointerException

  • 出现逻辑错误或不可预知的行为

为什么可以通过 静态内部类 实现懒汉式 单例模式

java 复制代码
public class Singleton {     
    private Singleton() {}     
    
    private static class SingletonHolder {         
        private static final Singleton INSTANCE = new Singleton();     
    }        
    
    public static Singleton getInstance() {              
        return SingletonHolder.INSTANCE; 
    }  
}

这依赖于 Java 类加载机制的特性:

  • JVM 在加载外部类的过程中不会加载静态内部类,只有内部类的属性 / 方法被调用时才会进行加载(懒汉式

  • JVM 在加载类的时候会自动保证线程安全,一个类在 JVM 中只会被加载一次

相关推荐
京师20万禁军教头8 分钟前
37面向对象(高级)-main方法
java
书源丶8 分钟前
三十五、Java 泛型——类型安全的「万能模板」
java·开发语言·安全
dovens32 分钟前
SpringBoot集成MQTT客户端
java·spring boot·后端
❀͜͡傀儡师37 分钟前
Spring Boot 集成 RocksDB 实战:打造高性能 KV 存储加速层
java·spring boot·后端·rocksdb
BENA ceic1 小时前
Spring 的三种注入方式?
java·数据库·spring
小雅痞1 小时前
[Java][Leetcode middle] 209. 长度最小的子数组
java·算法·leetcode
二哈赛车手1 小时前
新人笔记---项目中简易版的RAG检索后评测指标(@Recall ,Mrr..)实现
java·开发语言·笔记·spring·ai
做时间的朋友。1 小时前
精准核酸检测
java·数据结构·算法
许彰午1 小时前
CacheSQL(五):桥接篇
java·数据库·缓存·系统架构
ATCH IERV2 小时前
Java实战:Spring Boot application.yml配置文件详解
java·网络·spring boot