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

server/2025/3/31 6:35:59/

文章目录

  • 简介
  • 问题
    • 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获的静态方法来返回实例。单例的构造函数必须为私有。调用获取实例方法必须是获取单例对象的唯一方式。

http://www.ppmy.cn/server/179715.html

相关文章

webscoket爬虫之某旺(1)分析篇

文章仅供学习与交流!严禁用于任何商业与非法用途!否则由此产生的一切后果均与作者无关!! 什么是websocket爬虫 WebSocket爬虫是一种利用WebSocket协议进行数据抓取的工具或程序。WebSocket是一种在单个TCP连接上进行全双工通信的协议,常用于实时应用程序,如聊天应用、在…

Qt在模块依靠情况下资源文件名称和资源名称的使用限制

概述 在Qt中使用添加资源文件的时候,对于资源文件名称的定义,往往是较为随意的。 但是当涉及到Qt库依赖的时候,则可能需要遵守一定的规则,否则可能出现文件找不到或者错误加载的问题。 环境 环境名称Qt 版本系统版本LinuxQt 5.…

python 游戏开发cocos2d库安装与使用

Cocos2d-x 是一个广泛使用的开源游戏开发框架,支持多种编程语言,包括 Python。对于 Python 开发者来说,通常使用的是 Cocos2d-py 或者更现代的 Cocos2d-x 的 Python 绑定版本。这里我将指导你如何安装和开始使用 Cocos2d-py。 安装步骤 安装…

分布式爬虫框架Scrapy-Redis实战指南

引言 在当今数字化的时代背景下,互联网技术的蓬勃兴起极大地改变了旅游酒店业的运营模式与市场格局。作为旅游产业链中的关键一环,酒店业的兴衰与互联网技术的应用程度紧密相连。分布式爬虫技术,尤其是基于 Scrapy 框架的 Scrapy-Redis 扩展…

Redis解决缓存击穿问题——两种方法

目录 引言 解决办法 互斥锁(强一致,性能差) 逻辑过期(高可用,性能优) 设计逻辑过期时间 引言 缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这个时间点对…

Nginx 解决具有不安全、不正确或缺少 SameSite 属性的 Cookie方案

针对Nginx中Cookie的SameSite属性配置问题,以下是综合解决方案及注意事项: 一、基础配置方法 全局设置Cookie属性‌(适用于Nginx直接生成Cookie) 在nginx.conf的location块中通过add_header指令添加: add_header Se…

STM32--SPI通信讲解

前言 嘿,小伙伴们!今天咱们来聊聊STM32的SPI通信。SPI(Serial Peripheral Interface)是一种超常用的串行通信协议,特别适合微控制器和各种外设(比如传感器、存储器、显示屏)之间的通信。如果你…

论文阅读笔记——Diffuser,Diffusion Policy

Diffuser Diffuser 论文 将轨迹预测问题转化为基于扩散模型的条件生成问题,通过概率框架统一了动力学约束与目标优化。 轨迹表示(state,action): τ ( s 0 s 1 … … s T a 0 a 1 … … a T ) \tau \begin{pmatri…