单例模式(Singleton Pattern):深入解析与应用场景

news/2024/12/2 14:53:26/

一、什么是单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,其核心目标是确保一个类只有一个实例,并提供一个全局访问点。这种模式在许多场景下都非常有用,可以有效地控制资源的访问和管理。

二、单例模式的实现方式

1. 懒汉式(线程不安全)

java">public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
问题示例:

假设两个线程(线程A和线程B)几乎同时调用 getInstance(),流程如下:

  1. 线程A和线程B 都检查到 if (instance == null)
    • 因为 instance 还未被初始化,所以两个线程都进入了 if 块。
  2. 线程A执行 instance = new Singleton();
    • 线程A完成了对象的创建,instance 不再为 null
  3. 线程B执行 instance = new Singleton();
    • 因为线程B在检查 instance == null 时,instance 仍然为 null(线程A的赋值操作还未被线程B感知)。
    • 线程B再次创建了一个新实例,覆盖了线程A的结果。

最终,多个线程可能创建了多个实例

2. 懒汉式(线程安全)

java">public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

使用 synchronized 关键字确保线程安全:

3. 饿汉式(线程安全)

java">public class Singleton {private static final Singleton INSTANCE = new Singleton();private Singleton() {}public static Singleton getInstance() {return INSTANCE;}
}

private static final Singleton INSTANCE = new Singleton(); 意味着实例是在类加载时就被创建的
● 这种方式由Java虚拟机保证线程安全,因为静态成员变量的初始化是在类加载过程中进行的,是线程安全的

4. 双重检查锁(推荐)

java">public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

"双重检查锁定”策略来保证 懒加载线程安全,同时避免不必要的同步带来的性能开销。

第一次检查:if (instance == null)
  • 当多个线程并发调用 getInstance() 时,第一次检查是否实例化了 Singleton
  • 如果 instance 已经被创建,直接返回该实例,避免进入同步代码块,从而提高性能。
同步块:synchronized (Singleton.class)
  • 如果 instance 为空,则进入同步块,确保只有一个线程可以执行实例化的代码。
  • 这个 **synchronized** 确保了当多个线程同时进入该代码块时,只有一个线程能够创建 Singleton 实例。
第二次检查:if (instance == null)
  • 线程进入同步块后,第二次检查 instance 是否为空。因为可能多个线程并发到达同步块,第一个线程会创建实例,其他线程会被阻塞。
  • 第二次检查是为了防止多个线程在同步块中创建多个实例,确保 instance 只被初始化一次。
实例化:instance = new Singleton()
  • 当第一次和第二次检查都发现 instance 为空时,创建 Singleton 实例。
为什么使用 volatile 关键字?
  • volatile 确保了 instance 的变量在多线程环境下是可见的,避免了由于 指令重排缓存 导致实例初始化出现问题。
  • 如果没有 volatile,可能会出现某些线程看到一个未完全初始化的 instance 对象,从而导致 空指针异常不一致的状态

5. 静态内部类(推荐)

java">public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
为什么这种方式是线程安全的?
  • 类加载机制:Java 类加载是线程安全的。当 JVM 加载类时,它会保证类的初始化过程不会出现竞争条件。因此,SingletonHolder 类在被加载时,INSTANCE 只会被初始化一次,即使在多线程环境下也能保证唯一性。
  • 懒加载:静态内部类实现的单例模式通过 类初始化时 延迟实例化 Singleton,避免了提前创建对象的开销。
  • 性能:由于没有在每次调用 getInstance() 时都加锁,避免了同步带来的性能损失。因此,这种方式在保证线程安全的同时,也具备了更高的性能。

6.使用枚举创建

java">public enum Singleton {INSTANCE;// 可以在这里添加需要的方法public void doSomething() {System.out.println("Doing something...");}
}
优点:
  1. enum定义Singleton 被定义为一个枚举类型,只有一个 INSTANCE 成员。枚举类型的实例在类加载时就会被创建,因此可以确保只有一个 INSTANCE
  2. 线程安全:Java 枚举类型天然是线程安全的,因为 Java 在枚举类型初始化时,会确保其在 JVM 中的实例是唯一且线程安全的。
  3. 防止反射破坏单例:与其他单例模式不同,枚举类型能够自动防止反射破坏单例,因为枚举类型的构造方法是 私有的,且枚举实例的生成过程由 Java 虚拟机控制。
  4. 防止序列化破坏单例:Java 序列化机制中,枚举实例会被自动处理,避免了通过反序列化重新创建实例的问题。

三、线程安全的单例实现

实现线程安全的单例模式有多种方法:

  1. 使用synchronized关键字
  2. 使用双重检查锁(volatile + synchronized)
  3. 使用静态内部类
  4. 使用枚举(Java)

四、单例模式的使用场景

1.系统配置管理器:

java">public class SystemConfigManager {private static SystemConfigManager instance;private Properties configuration;private SystemConfigManager() {configuration = new Properties();loadConfiguration();}public static synchronized SystemConfigManager getInstance() {if (instance == null) {instance = new SystemConfigManager();}return instance;}private void loadConfiguration() {try (InputStream input = new FileInputStream("config.properties")) {configuration.load(input);} catch (IOException ex) {System.err.println("无法加载配置文件:" + ex.getMessage());}}public String getConfigValue(String key) {return configuration.getProperty(key);}public void setConfigValue(String key, String value) {configuration.setProperty(key, value);saveConfiguration();}private void saveConfiguration() {try (OutputStream output = new FileOutputStream("config.properties")) {configuration.store(output, "系统配置");} catch (IOException ex) {System.err.println("无法保存配置文件:" + ex.getMessage());}}
}

2.日志管理

java">public class Logger {private static Logger instance;private FileWriter fileWriter;private Logger() {try {fileWriter = new FileWriter("application.log", true);} catch (IOException e) {e.printStackTrace();}}public static synchronized Logger getInstance() {if (instance == null) {instance = new Logger();}return instance;}public void log(String message) {try {fileWriter.write(new Date() + ": " + message + "\n");fileWriter.flush();} catch (IOException e) {e.printStackTrace();}}
}// 使用示例
public class Application {public void performAction() {Logger logger = Logger.getInstance();logger.log("Action performed successfully");}
}

单例模式的典型应用领域

  1. 资源管理:连接池、缓存
  2. 全局状态控制:配置管理、系统设置
  3. 硬件交互:设备管理、外围设备控制
  4. 日志记录:集中式日志管理
  5. 服务协调:线程池、任务调度

http://www.ppmy.cn/news/1551785.html

相关文章

基于springboot在线租房和招聘平台源码和论文

如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统在线租房和招聘平台信息管理难度大,容错率低&#xf…

AWS 使用Lambda解压S3桶内压缩文件

创建两个桶 一个源桶 一个目标桶 创建Lambda函数 Lambda默认执行角色没有S3存储桶权限 添加Lambda和S3的所有权限 修改函数代码 https://github.com/pradip503/lambda-extract-zip/blob/master/code_snippet.py 修改源通和存储桶名称 添加触发器 选择S3源文件桶 修改函数执行…

spring boot打包fat jar

所谓fat jar就是包含所有依赖的jar以及其他开发的代码的jar包。可以通过java -jar xxx.jar直接启动运行,不需要部署到tomcat中间件就能运行。 接下来我们使用maven进行打包: (1)在需要带包的主模块的pom中添加build依赖&#xf…

selenium部署分布式 UI 自动化测试环境-Docker

一、根据selenium/hub官网的配置信息,进行配置。 How to run this image The Hub and Nodes will be created in the same network and they will recognize each other by their container name. A Docker network⁠ needs to be created as a first step.Create …

多种平台上安装部署调试Open5GS(二)

多种平台上安装部署调试Open5GS(二) Open5GS项目安装依赖open5GS源码编译webUI安装运行Open5GS 是一个功能完善的开源5G项目,具备5G、4G核心网功能,最新代码支持R17标准, 本系列文章介绍Open5GS在x86、ARM平台上的安装部署方法,并通过搭建UERANSIN、商用5G基站和终端两种…

VideoBooth: Diffusion-based Video Generation with Image Prompts

VideoBooth: Diffusion-based Video Generation with Image Prompts 概括 文章提出了一个视频生成模型VideoBooth,输入一张图片和一个文本提示词,即可输出保持图片中物体且符合文本提示词要求的视频。 方法 粗-细两阶段设计:1)…

【Linux】命令行参数与环境变量

目录 一、命令行参数1.1 命令行参数是什么?1.2 设计命令行参数的意义 二、环境变量2.1 环境变量的基本概念2.2 常见环境变量2.2.1 PATH2.2.1.1 通过命令行配置PATH2.2.1.2 使自己的程序像系统中指令一样运行 2.2.2 HOME2.2.3 PWD 2.3 查看操作系统中所有的环境变量&…

python股票数据分析(Pandas)练习

需求: 使用pandas读取一个CSV文件,文件内容包括股票名称、价格和交易量。完成以下任务: 找出价格最高的股票; 计算总交易量; 绘制价格折线图。 代码实现: import pandas as pd import matplotlib.pyplot …