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

23种设计模式--#2单例模式

一、简介

1. 什么是单例模式

        单例模式是设计模式中创建型模式的一种,它的核心思想是保证一个类在整个应用程序的生命周期中,只存在一个实例对象,并且这个实例对象能够被系统中的其他组件统一访问。就像现实生活中一个国家只有一个首都,一个公司只有一个 CEO 一样,在软件系统中,某些类的对象也只需要存在一个,以避免重复创建对象造成的资源浪费,或是多个实例同时存在导致的状态不一致等问题。

        单例模式看似简单,却是实际开发中应用非常广泛的设计模式之一。它通过对类的实例化过程进行严格控制,确保无论在什么情况下,都只能通过特定的方式获取到该类的唯一实例,从而为系统的稳定性和高效性提供保障。

2. 单例模式的核心特点

单例模式的核心特点可以概括为以下三点:

        确保唯一实例:这是单例模式最核心的特性。单例类会通过内部机制阻止外部通过常规的构造方法创建多个实例,保证在整个系统运行过程中,该类始终只有一个实例存在。例如在多线程环境下,无论多少个线程尝试获取该类的实例,最终得到的都是同一个对象,避免了因多实例存在而引发的资源竞争、状态混乱等问题。

        自行实例化:单例类不会依赖外部类来创建自身的实例,而是由类自身负责完成实例的创建。这种 “自给自足” 的特性使得单例类的实例化过程更加可控,减少了外部因素对实例创建的干扰。具体来说,单例类会在内部定义一个静态的实例对象,并通过特定的逻辑在合适的时机完成初始化,不需要外部通过new关键字或其他创建对象的方式来生成实例。

        向整个系统提供实例:单例类会提供一个全局访问点,让系统中的其他类或模块都能方便地获取到该唯一实例。这个访问点通常是一个静态的方法,例如getInstance(),外部通过调用这个方法就能得到单例对象,无需关心实例的创建细节。这种全局可访问的特性,使得单例对象可以作为系统中的 “全局工具”,承担起全局配置管理、跨模块数据共享等职责。

3.适用场景

        单例模式适用于需要确保系统中某个类只有一个实例的场景,例如配置管理器、日志记录器、数据库连接池或线程池等,这些场景中多个实例可能导致资源浪费、数据不一致或性能问题,通过提供一个全局访问点实现实例的复用和统一管理。

二、单例模式的实现方式

单例模式的实现有多种方式

饿汉式:类加载就会导致该单实例对象被创建。
​懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。

1、懒汉式,线程不安全

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程

优点

  • 延迟加载:实例仅在首次调用getInstance()方法时创建,避免了类加载时的资源浪费,适合实例初始化成本较高的场景。

缺点

  • 线程不安全:在多线程环境下,若多个线程同时进入if (instance == null)判断,可能会创建多个实例,破坏单例的唯一性。例如线程 A 和线程 B 同时检测到instance为 null,线程 A 先创建实例,线程 B 未感知到已创建的实例,会再次创建新实例。
public class Singleton {  private static Singleton instance;  private Singleton (){}  public static Singleton getInstance() {  if (instance == null) {  instance = new Singleton();  }  return instance;  }  
}
2、懒汉式,线程安全

能够在多线程中很好的工作,但是,效率很低

优点

  • 线程安全:通过synchronized关键字保证了多线程环境下只有一个线程能进入getInstance()方法,避免了多实例问题。
  • 延迟加载:保留了懒汉式延迟初始化的特性,仅在首次使用时创建实例。

缺点

  • 性能较差:每次调用getInstance()方法都需要获取同步锁,即使实例已经创建,后续所有调用仍需等待锁释放,在高并发场景下会显著影响性能。
public class Singleton {  private static Singleton instance;  private Singleton (){}  public static synchronized Singleton getInstance() {  if (instance == null) {  instance = new Singleton();  }  return instance;  }  
}
3、饿汉式

这种方式比较常用,但容易产生垃圾对象。

优点

  • 线程安全:由于实例在类加载时就已创建,且 Java 类加载机制保证了线程安全性,因此无需担心多线程环境下的实例唯一性问题。
  • 实现简单:代码逻辑清晰,无需复杂的同步或延迟加载逻辑。

缺点

  • 可能造成资源浪费:无论该实例是否被使用,都会在类加载时初始化,若实例占用大量资源(如内存、数据库连接等),且程序运行过程中始终未使用,则会造成不必要的资源消耗。
public class Singleton {  private static Singleton instance = new Singleton();  private Singleton (){}  public static Singleton getInstance() {  return instance;  }  
}
4、双检锁/双重校验锁

双重检查锁定(Double-Checked Locking)是对同步方法的优化,通过减少同步范围提升性能,同时保证线程安全。

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;  }  
}

优点

  • 线程安全:通过双重检查和同步块,确保多线程环境下仅创建一个实例。
  • 延迟加载:实例在首次调用时初始化,避免资源浪费。
  • 性能较好:仅在实例未初始化时进行同步,实例创建后无需进入同步块,大幅减少了锁竞争。

注意事项

  • 必须使用volatile关键字修饰实例变量:instance = new DoubleCheckedLockingSingleton()可分解为三步(分配内存、初始化对象、将引用指向内存),若不加volatile,可能因指令重排序导致其他线程获取到未初始化的实例。volatile可禁止指令重排序,保证实例初始化完成后才被其他线程可见。

5. 静态内部类

静态内部类实现方式利用了 Java 类加载机制的特性,兼顾了延迟加载和线程安全,且无需显式同步。

public class StaticInnerClassSingleton {// 私有构造方法private StaticInnerClassSingleton() {}// 静态内部类,仅在被调用时才会加载private static class InnerClass {private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();}// 公共访问方法,通过内部类获取实例public static StaticInnerClassSingleton getInstance() {return InnerClass.instance;}
}

优点

  • 线程安全:Java 类加载机制保证了静态内部类的加载过程是线程安全的,因此实例初始化过程不会出现多线程竞争。
  • 延迟加载:静态内部类InnerClass仅在getInstance()方法被调用时才会加载,实现了实例的延迟初始化。
  • 性能好:无需同步机制,避免了锁带来的性能损耗。

原理分析

  • 外部类StaticInnerClassSingleton加载时,内部类InnerClass不会被加载,因此实例不会被初始化。
  • 当调用getInstance()时,InnerClass被加载,其静态变量instance被初始化,且类加载过程由 JVM 保证线程安全,因此只会创建一个实例。

6、枚举

枚举是 Java 5 引入的特性,用枚举实现单例是一种简洁且安全的方式,由 JVM 天然保证单例特性。

public enum EnumSingleton {// 枚举实例,全局唯一INSTANCE;// 枚举类可以定义其他方法public void doSomething() {// 业务逻辑}
}

优点

  • 线程安全:JVM 保证枚举实例的创建是线程安全的,且在任何情况下都只会有一个实例。
  • 防止反射和序列化破坏:枚举的构造方法由 JVM 控制,反射无法通过newInstance()创建实例;序列化时枚举实例不会被重新创建,反序列化后仍为原实例。
  • 简洁性:代码量极少,无需手动处理同步、延迟加载等问题,可读性高。

适用场景

  • 适合对安全性要求高的场景,如全局配置、核心工具类等。
  • 由于枚举类型本身的特性,不适合需要继承的场景(枚举类默认继承Enum,Java 不支持多继承)。
http://www.lryc.cn/news/591090.html

相关文章:

  • git的cherry-pick
  • Py-Clipboard :iOS与Windows互相共享剪贴板(半自动)
  • AI+医疗!VR和MR解剖学和针灸平台,智能时代如何重塑健康未来
  • vue3实现web端和小程序端个人签名
  • [RAG] LLM 交互层 | 适配器模式 | 文档解析器(`docling`库, CNN, OCR, OpenCV)
  • docker安装与简单项目上手
  • 如何实现微信小程序引导组件【添加到我的小程序】+ 附源码
  • wx小程序原生开发使用高德地图api
  • 大语言模型任务分解与汇总:从认知瓶颈到系统化解决方案
  • 分布式分片策略中,分片数量的评估与选择
  • SAP-ABAP:SAP的‘cl_http_utility=>escape_url‘对URL进行安全编码方法详解
  • 2025毫米波雷达技术白皮书:智能汽车与物联网的感知核心
  • 【web安全】DVWA存储型XSS分析与利用
  • 【Linux系统】进程地址空间
  • 一款基于PHP开发的不良事件上报系统源码,适用于医院安全管理。系统提供10类事件类别、50余种表单,支持在线填报、匿名上报及紧急报告。
  • 亚马逊广告进阶指南:广告成本预算怎么设置合理
  • Ubuntu20.04 安装qt5.12.8
  • Unity_通过鼠标点击屏幕移动屏幕里的一个对象
  • Django 实战:静态文件与媒体文件从开发配置到生产部署
  • 贴吧项目总结二
  • 基于Rust Softplus 函数实践方法
  • 【项目经验】小智ai源码学习记录
  • Webpack5 新特性与详细配置指南
  • 基于LSTM的机场天气分析及模型预测
  • Python eval函数详解 - 用法、风险与安全替代方案
  • Go语言学习日志(一)
  • Python应用进阶DAY7--面向对象编程基本特性和super函数
  • 电子电路中的电压符号命名约定
  • FreeSWITCH配置文件解析(6) mod_format_cdr 话单中字段解析
  • 浅谈自动化设计最常用的三款软件catia,eplan,autocad