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

【设计模式】单例模式详解及应用实例

单例模式(Singleton Pattern)是一种创建型设计模式,保证一个类在整个程序的生命周期中只有一个实例,并提供一个全局访问点。单例模式广泛用于需要全局唯一实例的场景,比如数据库连接池、日志对象、线程池等。

单例模式的特点

  1. 唯一性:确保一个类只有一个实例。
  2. 延迟实例化:通常使用懒加载机制,即在第一次使用时才创建实例。
  3. 全局访问:提供一个全局访问点供外部获取该实例。

单例模式的实现方式

单例模式有多种实现方式,常见的有以下几种:

1. 懒汉式(Lazy Initialization)

懒汉式的单例模式是延迟初始化的,实例在第一次使用时才创建。这种方式的缺点是线程不安全。

public class SingletonLazy {private static SingletonLazy instance;// 私有化构造函数,防止外部实例化private SingletonLazy() {}// 获取单例对象的静态方法public static SingletonLazy getInstance() {if (instance == null) {instance = new SingletonLazy();}return instance;}
}

缺点: 懒汉式在多线程环境下可能会创建多个实例,因为线程可能会同时通过 if (instance == null) 条件。

2. 线程安全的懒汉式(Synchronized Method)

为了在多线程环境下保证实例的唯一性,可以使用 synchronized 关键字使方法同步。

public class SingletonLazyThreadSafe {private static SingletonLazyThreadSafe instance;private SingletonLazyThreadSafe() {}// 同步方法保证线程安全public static synchronized SingletonLazyThreadSafe getInstance() {if (instance == null) {instance = new SingletonLazyThreadSafe();}return instance;}
}

缺点: 虽然线程安全,但由于 synchronized 关键字的使用,每次调用 getInstance 都会产生一定的性能开销。

3. 双重检查锁定(Double-Checked Locking)

双重检查锁定在性能和线程安全之间做了折中。通过在实例初始化前后都进行检查,减少同步的开销。

public class SingletonDCL {// volatile 保证变量的可见性和有序性private static volatile SingletonDCL instance;private SingletonDCL() {}public static SingletonDCL getInstance() {if (instance == null) { // 第一次检查synchronized (SingletonDCL.class) {if (instance == null) { // 第二次检查instance = new SingletonDCL();}}}return instance;}
}

优点: 在多线程环境中高效且线程安全。

4. 静态内部类(Static Inner Class)

利用 Java 的类加载机制实现懒加载,静态内部类方式在类加载时不会立即实例化,而是在调用 getInstance() 时才加载内部类并创建实例。

public class SingletonStaticInnerClass {private SingletonStaticInnerClass() {}// 静态内部类持有 Singleton 的唯一实例private static class SingletonHolder {private static final SingletonStaticInnerClass INSTANCE = new SingletonStaticInnerClass();}public static SingletonStaticInnerClass getInstance() {return SingletonHolder.INSTANCE;}
}

优点: 线程安全,且没有性能开销,推荐使用。

5. 枚举(Enum Singleton)

使用枚举实现单例是一种比较优雅的方式,因为 Java 枚举本身是线程安全的,并且只能有一个实例。

public enum SingletonEnum {INSTANCE;public void someMethod() {// 业务逻辑}
}

优点: 实现简单,枚举保证了线程安全且防止了反序列化破坏单例。

单例模式使用场景

  • 配置类:当一个类只需要一个实例来提供全局配置时,比如读取配置文件的类。
  • 数据库连接池:只需要一个连接池对象来管理数据库连接。
  • 日志类:应用程序中通常需要一个全局的日志管理类。
  • 线程池:应用程序只需要一个线程池来管理线程。

单例模式的使用技巧

  1. 防止反射破坏单例: 通过私有化构造器的方式可以阻止外部类直接使用 new 创建实例,但通过反射可以访问私有构造器破坏单例。为了解决这个问题,可以在构造器内加一个判断:

    private Singleton() {if (instance != null) {throw new RuntimeException("单例模式被破坏");}
    }
    
  2. 防止序列化破坏单例: 如果单例类实现了 Serializable 接口,在反序列化时可能会创建新的实例。为了解决这个问题,可以在类中实现 readResolve 方法:

    private Object readResolve() {return instance;
    }
    
  3. 线程安全优化: 双重检查锁定(DCL)方式在保证线程安全的前提下,提高了性能,是多线程环境下常用的实现方式。

  4. 枚举单例推荐: 使用枚举实现单例模式可以简化代码,保证线程安全,并且防止反射和序列化破坏单例,因此被认为是最推荐的方式。

单例模式的实际实例

假设我们需要一个日志管理器,该管理器只允许创建一个实例,并能通过全局访问点进行日志记录。

public class LoggerSingleton {private static LoggerSingleton instance;private LoggerSingleton() {}// 双重检查锁定获取单例实例public static LoggerSingleton getInstance() {if (instance == null) {synchronized (LoggerSingleton.class) {if (instance == null) {instance = new LoggerSingleton();}}}return instance;}public void log(String message) {System.out.println("Log: " + message);}
}public class Main {public static void main(String[] args) {LoggerSingleton logger = LoggerSingleton.getInstance();logger.log("This is a singleton logger.");}
}

总结

  • 单例模式提供了确保类实例唯一的有效方案,适用于全局共享资源。
  • 常见实现方式包括懒汉式、双重检查锁定和静态内部类等。
  • 推荐使用枚举实现单例,因为它天然防止了反射和序列化破坏单例的风险。
http://www.lryc.cn/news/439937.html

相关文章:

  • 学习图解算法 使用C语言
  • 基于Netty实现TCP客户端:封装断线重连、连接保持
  • 基于形状记忆聚合物的折纸超结构
  • 前端用html写excel文件直接打开
  • FastText 和 Faiss 的初探了解
  • 微服务保护学习笔记(五)Sentinel授权规则、获取origin、自定义异常结果、规则持久化
  • YOLOv8目标检测模型——遥感小目标检测经验分享
  • 构建响应式 Web 应用:Vue.js 基础指南
  • 计算机毕业设计选题推荐-在线投票系统-Java/Python项目实战
  • 【C/C++】程序的构建(编译)过程概述
  • ElasticSearch-2-核心语法集群高可用实战-Week2
  • STM的CAN通信学习
  • 【高等数学学习记录】函数
  • 【springboot过ingress后无法获取X-Forwarded-For头信息】
  • 表格标记<table>
  • Rust练手项目,写个有趣的小工具定时从一言网获取一段有趣的话并推送通知
  • 【隐私计算】Paillier半同态加密算法
  • 判断数字的奇偶[中秋快乐~]
  • 文件操作及重定向详解
  • 鸿蒙next json解析 ArkUI 带你玩转 arkts json解析
  • 东土科技加码芯片业务投资,携手神经元共建新型工业生态
  • 指纹与指甲检测系统源码分享
  • C++3D迷宫
  • 跨界融合,GIS如何赋能游戏商业——以《黑神话:悟空》为例
  • 【计网】从零开始使用TCP进行socket编程 --- 客户端与服务端的通信实现
  • Imagen:重塑图像生成领域的革命性突破
  • Golang | Leetcode Golang题解之第402题移掉K位数字
  • c++ gtsam/inference/Symbol.h 详细介绍
  • apache文件共享和访问控制
  • LeetCode 2398.预算内的最多机器人数目:滑动窗口+单调队列——思路清晰的一篇题解