当前位置: 首页 > news >正文

单例模式-3-双检锁/双重校验锁(DCL,即 double-checked locking)

以下是对这段代码的详细解释,这是一种实现 双重检查锁定(Double-Checked Locking) 的单例模式:


1. 类的定义

public class Singleton {private volatile static Singleton singleton;private Singleton() {}public static Singleton getSingleton() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}

2. 代码逐步解析

2.1. private volatile static Singleton singleton;
  • private:确保 singleton 变量只能在 Singleton 类内部访问,防止外部直接修改。
  • static:确保 singleton 是类级别的变量,所有实例共享同一个变量。
  • volatile:保证多线程环境下的可见性和有序性。
    • 可见性:当一个线程修改了 singleton 的值,其他线程能够立即看到修改后的值。
    • 有序性:防止指令重排序问题(即对象未完全初始化时,其他线程可能访问到未初始化的对象)。

2.2. private Singleton() {}
  • 将构造方法声明为 private,防止外部通过 new 关键字创建实例。
  • 这是单例模式的核心,确保只能通过类的静态方法获取实例。

2.3. public static Singleton getSingleton()

这是获取单例实例的公共方法,包含了双重检查锁定的逻辑。


2.4. if (singleton == null)
  • 第一次检查:在进入同步块之前,检查 singleton 是否为 null
    • 如果 singleton 已经被初始化,则直接返回实例,避免不必要的同步开销。
    • 如果 singletonnull,说明实例尚未创建,进入同步块。

2.5. synchronized (Singleton.class)
  • 同步块:对 Singleton.class 加锁,确保只有一个线程能够进入同步块,避免多个线程同时创建实例。
  • 这是双重检查锁定的关键,确保线程安全。

2.6. if (singleton == null)
  • 第二次检查:进入同步块后,再次检查 singleton 是否为 null
    • 这是因为可能有多个线程在第一次检查时通过了 if (singleton == null),但只有一个线程能够进入同步块。
    • 如果没有第二次检查,可能会导致多个线程创建多个实例。

2.7. singleton = new Singleton();
  • 创建单例实例。
  • 由于 volatile 的作用,确保对象初始化完成后,其他线程能够正确看到 singleton 的值。

2.8. return singleton;
  • 返回单例实例。

3. 为什么需要双重检查锁定?

双重检查锁定的目的是在保证线程安全的同时,尽量减少同步的开销:

  1. 第一次检查:避免每次调用 getSingleton() 都进入同步块,提高性能。
  2. 第二次检查:确保在多线程环境下,只有一个线程能够创建实例,保证线程安全。

4. 为什么需要 volatile

在没有 volatile 的情况下,可能会出现指令重排序问题:

  1. singleton = new Singleton(); 不是一个原子操作,实际上分为三步:
    • 分配内存。
    • 调用构造函数初始化对象。
    • 将对象引用赋值给 singleton
  2. 如果发生指令重排序,可能会导致以下情况:
    • 线程 A 执行了第 1 步和第 3 步,但尚未完成第 2 步。
    • 线程 B 访问到未完全初始化的对象,可能导致程序崩溃。
  3. 使用 volatile 可以禁止指令重排序,确保对象初始化的有序性。

5. 优点

  • 线程安全:通过双重检查和同步块,确保多线程环境下的安全性。
  • 性能优化:避免每次获取实例时都加锁,提升性能。
  • 延迟加载:实例在第一次使用时才会被创建,节省内存。

6. 缺点

  • 实现较复杂,容易出错。
  • 在某些情况下,可能会增加代码的维护成本。

7. 总结

这段代码是经典的双重检查锁定单例模式的实现,适用于多线程环境下的单例创建。通过 volatile 和双重检查机制,既保证了线程安全,又优化了性能,是一种高效的单例模式实现方式。

相关文章:

  • Spark-SQL核心编程语言
  • 详解@JsonFormat和@DateTimeFormat注解:处理日期格式化的利器
  • Bright+Data网页解锁器在旅游行业的创新实践
  • 【深入C++多态:基于消息解析器的设计、实现与剖析】
  • T4P: Test-Time Training of Trajectory Prediction
  • 回溯算法:List 还是 ArrayList?一个深拷贝引发的思考
  • Jenkins 代理自动化-dotnet程序
  • 配置HADOOP_HOME环境变量和maven_HOME环境变量
  • 线代第二章矩阵第二课:矩阵的加法、减法、数乘
  • Python+Playwright:编写自动化测试的避坑策略
  • Mac系统升级node.js版本和npm版本并安装pnpm
  • Node.js Session 原理简单介绍 + 示例代码
  • Sui 的工具生态简化了游戏开发者的 Web3 集成流程
  • 技术与情感交织的一生 (六)
  • My Diary Pro:记录生活,珍藏回忆
  • Android NDK 编译 so 文件 抹除导出符号 反逆向
  • 如何争取高层对项目的支持
  • Docker安装 (centos)
  • GitHub 封禁中国 IP:影响、原因及应对
  • 浏览器自动化检测对抗:修改navigator.webdriver属性的底层实现
  • 中国国家电影局与俄罗斯文化部签署电影合作文件
  • 经彩申城!上海网络大V沙龙活动走进闵行
  • 上海质子重离子医院二期项目启动,有望成为全世界最大粒子治疗中心
  • 进化版大巴黎通杀英超,那个男人后悔了吗
  • 明星站台“胖都来”背后:百元起录视频,20万可请顶流
  • 上任后首访,德国总理与法国总统举行会晤