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

23种设计模式-创建型模式-单例

文章目录

  • 简介
  • 问题
    • 1. 确保一个类只有一个实例
    • 2. 为该实例提供全局访问点
  • 解决方案
  • 示例
    • 重构前:
    • 重构后:
  • 拓展
    • volatile 在单例模式中的双重作用
  • 总结

简介

单例是一种创建型设计模式,它可以确保一个类只有一个实例,同时为该实例提供全局访问点。

问题

单例模式同时解决了两个问题:

1. 确保一个类只有一个实例

最常见的场景是控制对某些共享资源(例如数据库或文件)的访问。假设你已经创建了一个对象,又要创建一个相同类的对象。你不会得到一个新的对象,而是会得到你已经创建的对象。这种行为是无法通过常规构造函数实现,因为构造函数调用在设计上必须始终返回一个新对象。

2. 为该实例提供全局访问点

全局变量非常方便,但很不安全,因为任何代码都可能覆盖这些变量的内容并使程序崩溃。单例模式类似全局变量,允许你从程序中的任何位置访问某个对象。但是,它还可以保护这个实例不被其他代码覆盖。还有一点,为了不让实现问题1 的代码分散在各个地方,要把它限制在一个类中,特别是当你的其余代码已经依赖了它的时候。

解决方案

所有 Singleton 的实现都有这样两个共同的步骤:

  • 把默认构造函数设为私有,防止其他对象使用new创建它。
  • 创建一个充当构造函数的静态创建方法。这个方法会调用私有构造函数来创建一个对象并把它缓存在静态字段中。这个方法的所有后续调用都会返回缓存好的对象。

如果你的代码能访问 Singleton 类,那么它就可以调用 Singleton 的静态方法。无论何时调用该方法,都会返回相同的对象。

示例

数据库连接

重构前:

class DBUtil {
    public Connection getConn() {
        return DriverManager.getConnection(URL); // 每次新建连接消耗500ms+
    }
}

// 调用端
new DBUtil().getConn().execute("SELECT..."); 
new DBUtil().getConn().execute("UPDATE..."); // 产生两个独立连接

重构后:

public class Database {
    private static volatile Database instance; 
    private Connection connection;

    // 私有化构造并建立物理连接
    private Database() {
        this.connection = DriverManager.getConnection(JDBC_URL); // 真实连接建立
    }

    // 双重检查锁定实现线程安全
    public static Database getInstance() {
        if (instance == null) { 
            synchronized (Database.class) {
                if (instance == null) {
                    instance = new Database(); 
                }
            }
        }
        return instance;
    }

    // 统一入口方法(可扩展缓存逻辑)
    public ResultSet query(String sql) {
        return connection.createStatement().executeQuery(sql); // 所有SQL通过单连接执行
    }
}

拓展

volatile 在单例模式中的双重作用

  1. 可见性保证(Visibility)
    阻止线程的本地缓存与主内存数据不同步,确保所有线程读取到的是最新实例状态。
  2. 禁止指令重排序(Happens-Before)
    消除 JVM 级别可能的危险优化(非原子化对象构造的三步指令):
未加 volatile 时的风险时序:
A线程: 分配内存 → 写入未初始化的对象引用(指令排序导致)
B线程: 获取到非空引用 → 访问未完成初始化的对象(空指针异常)

volatile 强制时序:
分配内存 → 初始化对象 → 写入引用(三步骤原子性可见)

总结

在这里插入图片描述

  1. 单例(Sin­gle­ton)类:声明了一个叫做get­Instance获的静态方法来返回实例。单例的构造函数必须为私有。调用获取实例方法必须是获取单例对象的唯一方式。

相关文章:

  • WPF 与 C# 开发深度剖析
  • 如何避免权限分配不合理导致的信息安全风险?
  • 【天梯赛】L2-012(实战反思代码实现)
  • 压测工具开发(一)——使用Qt Designer构建简单界面
  • Java编程思想:为何有时要将子类对象赋值给父类引用
  • 六级备考 词汇量积累(day11)
  • DNS域名解析服务
  • 【区块链安全 | 第一篇】密码学原理
  • C++初阶入门基础二——类和对象(中)
  • 避免踩坑!查收查引常见问题解答
  • C语言动态内存管理深度解析与嵌入式开发实战
  • 12_JavaScript_实现日期
  • dfs(深度优先)——太抽象了
  • 【新能源汽车实验室设备控制:PLC与单片机选型指南(深度解析+实战案例)】
  • AI数字人直播系统
  • java中的枚举类型和c,c++的有区别吗?c,c++的枚举,结构体,联合体,三种数据有什么区别和联系
  • 基于DrissionPage的TB商品信息采集与可视化分析
  • 《大语言模型》学习笔记(四)--Transformer 模型
  • 数据库 第一章 MySql基础(1)
  • Thales靶机渗透攻略
  • 乌克兰官员与法德英美四国官员举行会谈
  • 高新波任西安电子科技大学校长
  • 巴菲特最新调仓:一季度大幅抛售银行股,再现保密仓位
  • 标普500指数连涨四日,大型科技股多数下跌
  • 明查| 新一代AI诊疗系统可3秒筛查13种癌症?没有证据
  • 跨文化戏剧的拓荒者与传承者——洪深与复旦剧社的百年回响