JAVA面试-并发篇 03-使用synchronized doublecheck实现单例有什么坑

DCL(Double-Check Locking 双重检查锁)单例的坑

一、核心结论

经典DCL最致命的是volatile缺失导致的半初始化对象空指针。

二、错误 vs 正确完整写法

❌ 错误写法(90%的人会写错,缺volatile)

java 复制代码
public class Singleton {
    // 致命错误:缺少volatile关键字
    private static Singleton instance;
    
    // 私有构造方法
    private Singleton() {}
    
    public static Singleton getInstance() {
        // 第一次检查(无锁)
        if (instance == null) {
            // 加锁
            synchronized (Singleton.class) {
                // 第二次检查(有锁)
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

✅ 正确写法(加volatile禁止指令重排序)

java 复制代码
public class Singleton {
    // 关键:加上volatile关键字
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

三、最致命坑:指令重排序

对象创建的两种执行顺序

复制代码
┌───────────── 正常顺序(安全) ─────────────┐
│ 1. 分配对象内存空间                        │
│ 2. 初始化对象(执行构造方法)              │
│ 3. 将instance引用指向该内存地址            │
└───────────────────────────────────────────┘

┌───────────── 重排序后(危险) ─────────────┐
│ 1. 分配对象内存空间                        │
│ 3. 将instance引用指向该内存地址            │
│ 2. 初始化对象(执行构造方法)              │
└───────────────────────────────────────────┘

灾难发生完整流程

复制代码
线程A执行 instance = new Singleton()
        ↓
JVM发生指令重排序:先执行步骤3,后执行步骤2
        ↓
instance ≠ null,但对象内部数据全是默认值(未初始化)
        ↓
线程B执行第一次检查:发现instance≠null,直接返回
        ↓
线程B使用这个"半成品"对象 → 抛出NullPointerException
相关推荐
学长毕设3 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
zzu123zsw4 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
Yuzhiyuxia4 小时前
【AI Agent面试系列】AI Agent记忆机制知识点1
人工智能·面试·职场和发展
源码集结号5 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
论文复现现场5 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠5 小时前
泛微OAe9-后端二开异步调用
java
秋天的怀念_David6 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放6 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog6 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友
用户3721574261357 小时前
Java 操作 Word 脚注和尾注:添加、读取与删除
java