0

0

如何实现单例模式的DCL双重检查锁定_volatile在防止重排序中的应用

P粉602998670

P粉602998670

发布时间:2026-03-03 10:46:48

|

857人浏览过

|

来源于php中文网

原创

dcl单例必须用volatile,因为jvm可能重排序对象初始化的三步(分配内存、初始化字段、赋值引用),导致其他线程获取到未构造完成的实例;volatile禁止重排序并保证可见性。

如何实现单例模式的dcl双重检查锁定_volatile在防止重排序中的应用

为什么 DCL 单例里必须用 volatile

因为不加 volatile,JVM 可能重排序对象初始化步骤,导致其他线程拿到未构造完成的实例。这不是理论风险——在 x86 以外的平台(比如 ARM)、或高并发压测下,getInstance() 真的会返回一个 new Singleton() 还没执行完构造函数的对象。

关键在于:JVM 允许将 memory = new Singleton() 拆成三步:
① 分配内存;② 初始化字段(含调用构造函数);③ 把引用赋值给静态变量 instance
但②和③可能被重排序,而 volatile 禁止这种重排序,并保证后续读操作能看到完整的初始化结果。

  • 只加 synchronized 不加 volatile —— 锁内安全,但锁外读取仍可能看到半初始化对象
  • volatile 本身不保证原子性,但它提供了「写后读可见」+「禁止指令重排」两个必要保障
  • Java 5+ 才真正修复了 volatile 的语义,老版本(如 Java 1.4)无效

Java 中正确实现 DCL 单例的写法

核心是:静态变量声明带 volatile,第一次判空不加锁,第二次判空在同步块内再检查。

MyMap AI
MyMap AI

使用AI将想法转化为图表

下载
public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {                    // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) {            // 第二次检查
                    instance = new Singleton();    // 这一行必须是 volatile 写
                }
            }
        }
        return instance;
    }
}
  • instance 必须是 static volatile,缺一不可
  • 构造函数设为 private,防止反射绕过(反射另需防护,不在 DCL 范围内)
  • 不要在 getInstance() 里做耗时操作(比如 IO),否则锁会拖慢所有线程
  • 如果类有 final 字段,它们的初始化也受 volatile 写的「发生前」保证,是安全的

DCL 在哪些场景下反而更危险

不是所有单例都适合 DCL。一旦对象初始化过程复杂,DCL 容易掩盖问题。

  • 构造函数抛异常:instance 仍为 null,下次调用又会尝试创建,但异常细节可能丢失
  • 依赖外部状态(如配置、Spring 上下文):DCL 假设构造是幂等的,实际未必成立
  • 子类继承单例类:若子类也实现 DCL,父类 instance 和子类 instance 是两个变量,容易误用
  • Android 中 Dalvik/ART 对 volatile 重排序行为曾有差异,低版本系统上偶发 crash

比 DCL 更简单且安全的替代方案

除非对启动性能极度敏感,否则直接用静态内部类或枚举更省心。

  • 静态内部类方式:SingletonHolder 类只在 getInstance() 第一次调用时加载,天然线程安全,无重排序风险,且无同步开销
  • 枚举方式:public enum Singleton { INSTANCE; } —— JVM 保证枚举实例初始化绝对线程安全,还能防反射和反序列化攻击
  • Spring 管理的 @Scope("singleton") Bean 默认就是单例,无需手写 DCL

真正需要 DCL 的场合其实很少:通常是底层工具类(如日志器、配置加载器),且明确要求懒加载 + 高并发 + 无框架依赖。其它情况,写错比写对更容易。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

149

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

87

2026.01.26

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

251

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

988

2024.03.01

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

251

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

988

2024.03.01

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

251

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

988

2024.03.01

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

2

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号