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

设计模式之:单例模式

文章目录

    • 什么是单例模式?
    • 核心思想
    • 模式结构
    • 单例模式的多种实现方式
      • 1. 饿汉式单例(Eager Initialization)
      • 2. 懒汉式单例(Lazy Initialization)
      • 3. 双重检查锁单例(Double-Checked Locking)
      • 4. 静态内部类单例(Holder Pattern)
      • 5. 枚举单例(Enum Singleton)
    • 完整示例:配置管理器单例
      • 1. 配置管理器单例类
      • 2. 数据库连接池单例
      • 3. 客户端使用示例
    • 单例模式的优点
      • 1. 严格控制实例数量
      • 2. 全局访问点
      • 3. 延迟初始化
    • 单例模式的缺点
      • 1. 违反单一职责原则
      • 2. 难以测试
    • 适用场景
    • 最佳实践
      • 1. 优先使用枚举或静态内部类实现
      • 2. 考虑线程安全
      • 3. 防止反射和反序列化破坏
    • 总结

什么是单例模式?

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。单例模式是设计模式中最简单但也是最容易误用的模式之一。

核心思想

单例模式的核心思想是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 它通过控制对象的创建过程,确保在整个应用程序中只存在一个实例。

模式结构

单例模式包含一个核心角色:

  1. 单例类(Singleton):负责创建自己的唯一实例,并提供访问该实例的全局方法

单例模式的多种实现方式

1. 饿汉式单例(Eager Initialization)

/*** 饿汉式单例 - 线程安全* 在类加载时就完成实例化,避免了多线程同步问题*/
public class EagerSingleton {// 在类加载时立即实例化private static final EagerSingleton INSTANCE = new EagerSingleton();// 私有构造函数,防止外部实例化private EagerSingleton() {// 防止通过反射创建实例if (INSTANCE != null) {throw new RuntimeException("单例对象已被创建,禁止通过反射实例化");}System.out.println("饿汉式单例实例化完成");}// 全局访问点public static EagerSingleton getInstance() {return INSTANCE;}// 业务方法public void showMessage() {System.out.println("Hello from EagerSingleton!");}// 防止反序列化破坏单例private Object readResolve() {return INSTANCE;}
}

2. 懒汉式单例(Lazy Initialization)

/*** 懒汉式单例 - 基础版本(线程不安全)* 在第一次调用getInstance时才创建实例*/
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {System.out.println("懒汉式单例实例化完成");}// 线程不安全的实现public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}public void showMessage() {System.out.println("Hello from LazySingleton!");}
}/*** 懒汉式单例 - 线程安全版本(同步方法)* 通过synchronized保证线程安全,但性能较差*/
public class ThreadSafeLazySingleton {private static ThreadSafeLazySingleton instance;private ThreadSafeLazySingleton() {System.out.println("线程安全懒汉式单例实例化完成");}// 使用synchronized保证线程安全,但每次访问都会同步,性能低public static synchronized ThreadSafeLazySingleton getInstance() {if (instance == null) {instance = new ThreadSafeLazySingleton();}return instance;}public void showMessage() {System.out.println("Hello from ThreadSafeLazySingleton!");}
}

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

/*** 双重检查锁单例 - 高性能线程安全版本* 既保证了线程安全,又提高了性能*/
public class DoubleCheckedLockingSingleton {// 使用volatile禁止指令重排序,保证可见性private static volatile DoubleCheckedLockingSingleton instance;private DoubleCheckedLockingSingleton() {System.out.println("双重检查锁单例实例化完成");}public static DoubleCheckedLockingSingleton getInstance() {// 第一次检查,避免不必要的同步if (instance == null) {// 同步代码块synchronized (DoubleCheckedLockingSingleton.class) {// 第二次检查,确保只有一个线程创建实例if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;}public void showMessage() {System.out.println("Hello from DoubleCheckedLockingSingleton!");}
}

4. 静态内部类单例(Holder Pattern)

/*** 静态内部类单例 - 推荐使用的实现方式* 结合了饿汉式的线程安全和懒汉式的延迟加载*/
public class InnerClassSingleton {private InnerClassSingleton() {System.out.println("静态内部类单例实例化完成");}// 静态内部类,在第一次被引用时才会加载private static class SingletonHolder {private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();}public static InnerClassSingleton getInstance() {return SingletonHolder.INSTANCE;}public void showMessage() {System.out.println("Hello from InnerClassSingleton!");}// 防止反序列化破坏单例private Object readResolve() {return SingletonHolder.INSTANCE;}
}

5. 枚举单例(Enum Singleton)

/*** 枚举单例 - Joshua Bloch推荐的方式*  Effective Java作者推荐的单例实现方式,绝对防止多实例*/
public enum EnumSingleton {INSTANCE;// 枚举的构造函数默认就是private的EnumSingleton() {System.out.println("枚举单例实例化完成");}// 业务方法public void showMessage() {System.out.println("Hello from EnumSingleton!");}// 可以添加其他业务方法public void doBusinessLogic() {System.out.println("执行业务逻辑...");}
}

完整示例:配置管理器单例

让我们通过一个完整的配置管理器示例来展示单例模式的实际应用。

1. 配置管理器单例类

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;/*** 配置管理器单例 - 实际应用示例* 在整个应用中只需要一个配置管理器实例*/
public class ConfigurationManager {// 使用静态内部类实现单例private static class Holder {private static final ConfigurationManager INSTANCE = new ConfigurationManager();}private final Map<String, String> configMap;private final Properties properties;private boolean initialized = false;private ConfigurationManager() {this.configMap = new HashMap<>();this.properties = new Properties();System.out.println("配置管理器单例创建完成");loadDefaultConfig();}public static ConfigurationManager getInstance() {return Holder.INSTANCE;}/*** 加载默认配置*/private void loadDefaultConfig() {// 模拟加载默认配置configMap.put("app.name", "MyApplication");configMap.put("app.version", "1.0.0");configMap.put("database.url", "jdbc:mysql://localhost:3306/mydb");configMap.put("database.username", "admin");configMap.put("server.port", "8080");configMap.put("log.level", "INFO");initialized = true;System.out.println("默认配置加载完成");}/*** 获取配置值*/public String getConfig(String key) {if (!initialized) {throw new IllegalStateException("配置管理器未初始化");}return configMap.get(key);}/*** 获取配置值,带默认值*/public String getConfig(String key, String defaultValue) {String value = getConfig(key);return value != null ? value : defaultValue;}/*** 设置配置值*/public void setConfig(String key, String value) {if (!initialized) {throw new IllegalStateException("配置管理器未初始化");}configMap.put(key, value);System.out.println("配置已更新: " + key + " = " + value);}/*** 检查配置是否存在*/public boolean containsConfig(String key) {return configMap.containsKey(key);}/*** 获取所有配置*/public Map<String, String> getAllConfigs() {return new HashMap<>(configMap); // 返回副本保护内部数据}/*** 重新加载配置*/public void reloadConfig() {configMap.clear();loadDefaultConfig();System.out.println("配置重新加载完成");}/*** 打印所有配置*/public void printAllConfigs() {System.out.println("\n=== 当前所有配置 ===");configMap.forEach((key, value) -> System.out.printf("%-20s: %s%n", key, value));System.out.println("===================\n");}
}

2. 数据库连接池单例

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;/*** 数据库连接池单例 - 另一个实际应用示例*/
public class DatabaseConnectionPool {private static volatile DatabaseConnectionPool instance;private final List<Connection> connectionPool;private final List<Connection> usedConnections;private static final int INITIAL_POOL_SIZE = 10;private static final int MAX_POOL_SIZE = 20;private final String url;private final String user;private final String password;private DatabaseConnectionPool() {this.connectionPool = new ArrayList<>();this.usedConnections = new ArrayList<>();// 从配置管理器获取数据库配置ConfigurationManager config = ConfigurationManager.getInstance();this.url = config.getConfig("database.url");this.user = config.getConfig("database.username");this.password = config.getConfig("database.password", "defaultpass");initializePool();System.out.println("数据库连接池初始化完成,初始连接数: " + INITIAL_POOL_SIZE);}public static DatabaseConnectionPool getInstance() {if (instance == null) {synchronized (DatabaseConnectionPool.class) {if (instance == null) {instance = new DatabaseConnectionPool();}}}return instance;}private void initializePool() {for (int i = 0; i < INITIAL_POOL_SIZE; i++) {try {Connection connection = createConnection();connectionPool.add(connection);} catch (SQLException e) {System.err.println("创建数据库连接失败: " + e.getMessage());}}}private Connection createConnection() throws SQLException {// 模拟创建数据库连接System.out.println("创建新的数据库连接");return DriverManager.getConnection(url, user, password);}public synchronized Connection getConnection() {if (connectionPool.isEmpty()) {if (usedConnections.size() < MAX_POOL_SIZE) {try {Connection connection = createConnection();usedConnections.add(connection);return connection;} catch (SQLException e) {throw new RuntimeException("无法创建新连接", e);}} else {throw new RuntimeException("连接池已满,无法获取连接");}}Connection connection = connectionPool.remove(connectionPool.size() - 1);usedConnections.add(connection);return connection;}public synchronized boolean releaseConnection(Connection connection) {connectionPool.add(connection);return usedConnections.remove(connection);}public synchronized void shutdown() {for (Connection connection : connectionPool) {try {connection.close();} catch (SQLException e) {System.err.println("关闭连接失败: " + e.getMessage());}}for (Connection connection : usedConnections) {try {connection.close();} catch (SQLException e) {System.err.println("关闭连接失败: " + e.getMessage());}}connectionPool.clear();usedConnections.clear();System.out.println("数据库连接池已关闭");}public int getAvailableConnectionsCount() {return connectionPool.size();}public int getUsedConnectionsCount() {return usedConnections.size();}
}

3. 客户端使用示例

/*** 单例模式客户端演示*/
public class SingletonPatternDemo {public static void main(String[] args) {System.out.println("=== 单例模式完整演示 ===\n");// 演示1: 各种单例实现方式demonstrateSingletonImplementations();// 演示2: 配置管理器单例使用demonstrateConfigurationManager();// 演示3: 数据库连接池单例使用demonstrateDatabaseConnectionPool();// 演示4: 多线程环境测试demonstrateThreadSafety();// 演示5: 单例破坏与防护demonstrateSingletonProtection();}/*** 演示不同的单例实现方式*/private static void demonstrateSingletonImplementations() {System.out.println("1. 不同单例实现方式演示:");System.out.println("-".repeat(50));// 饿汉式单例EagerSingleton eager1 = EagerSingleton.getInstance();EagerSingleton eager2 = EagerSingleton.getInstance();System.out.println("饿汉式单例是否相同: " + (eager1 == eager2));eager1.showMessage();// 懒汉式单例LazySingleton lazy1 = LazySingleton.getInstance();LazySingleton lazy2 = LazySingleton.getInstance();System.out.println("懒汉式单例是否相同: " + (lazy1 == lazy2));lazy1.showMessage();// 双重检查锁单例DoubleCheckedLockingSingleton dcl1 = DoubleCheckedLockingSingleton.getInstance();DoubleCheckedLockingSingleton dcl2 = DoubleCheckedLockingSingleton.getInstance();System.out.println("双重检查锁单例是否相同: " + (dcl1 == dcl2));dcl1.showMessage();// 静态内部类单例InnerClassSingleton inner1 = InnerClassSingleton.getInstance();InnerClassSingleton inner2 = InnerClassSingleton.getInstance();System.out.println("静态内部类单例是否相同: " + (inner1 == inner2));inner1.showMessage();// 枚举单例EnumSingleton enum1 = EnumSingleton.INSTANCE;EnumSingleton enum2 = EnumSingleton.INSTANCE;System.out.println("枚举单例是否相同: " + (enum1 == enum2));enum1.showMessage();enum1.doBusinessLogic();}/*** 演示配置管理器单例*/private static void demonstrateConfigurationManager() {System.out.println("\n2. 配置管理器单例演示:");System.out.println("-".repeat(50));// 获取配置管理器实例ConfigurationManager configManager = ConfigurationManager.getInstance();// 读取配置System.out.println("应用名称: " + configManager.getConfig("app.name"));System.out.println("数据库URL: " + configManager.getConfig("database.url"));System.out.println("服务器端口: " + configManager.getConfig("server.port"));// 设置新配置configManager.setConfig("cache.enabled", "true");configManager.setConfig("max.connections", "100");// 打印所有配置configManager.printAllConfigs();// 验证单例特性ConfigurationManager anotherInstance = ConfigurationManager.getInstance();System.out.println("配置管理器单例验证: " + (configManager == anotherInstance));}/*** 演示数据库连接池单例*/private static void demonstrateDatabaseConnectionPool() {System.out.println("\n3. 数据库连接池单例演示:");System.out.println("-".repeat(50));DatabaseConnectionPool pool1 = DatabaseConnectionPool.getInstance();DatabaseConnectionPool pool2 = DatabaseConnectionPool.getInstance();System.out.println("连接池单例验证: " + (pool1 == pool2));System.out.println("可用连接数: " + pool1.getAvailableConnectionsCount());System.out.println("已用连接数: " + pool1.getUsedConnectionsCount());// 模拟获取和释放连接Connection conn1 = pool1.getConnection();Connection conn2 = pool1.getConnection();System.out.println("获取2个连接后:");System.out.println("可用连接数: " + pool1.getAvailableConnectionsCount());System.out.println("已用连接数: " + pool1.getUsedConnectionsCount());pool1.releaseConnection(conn1);pool1.releaseConnection(conn2);System.out.println("释放2个连接后:");System.out.println("可用连接数: " + pool1.getAvailableConnectionsCount());System.out.println("已用连接数: " + pool1.getUsedConnectionsCount());}/*** 演示多线程环境下的单例安全性*/private static void demonstrateThreadSafety() {System.out.println("\n4. 多线程安全性演示:");System.out.println("-".repeat(50));int threadCount = 5;Thread[] threads = new Thread[threadCount];for (int i = 0; i < threadCount; i++) {final int threadId = i;threads[i] = new Thread(() -> {InnerClassSingleton singleton = InnerClassSingleton.getInstance();System.out.println("线程 " + threadId + " 获取单例: " + singleton.hashCode());});}for (Thread thread : threads) {thread.start();}for (Thread thread : threads) {try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("所有线程获取的单例哈希码相同,证明线程安全");}/*** 演示单例防护机制*/private static void demonstrateSingletonProtection() {System.out.println("\n5. 单例防护机制演示:");System.out.println("-".repeat(50));try {// 尝试通过反射破坏单例// 注意:这里只是演示,实际代码应该避免这样的操作System.out.println("尝试通过反射创建实例...");// 对于枚举单例,反射也无法创建新实例// 对于其他实现,可以在构造函数中添加防护代码} catch (Exception e) {System.out.println("反射破坏被阻止: " + e.getMessage());}}
}

单例模式的优点

1. 严格控制实例数量

// 确保整个应用中只有一个实例
ConfigurationManager config = ConfigurationManager.getInstance();
// 无论在哪里调用,得到的都是同一个实例

2. 全局访问点

// 提供统一的访问入口
DatabaseConnectionPool pool = DatabaseConnectionPool.getInstance();
// 便于管理和维护

3. 延迟初始化

// 静态内部类实现提供了延迟初始化的好处
// 只有在第一次调用getInstance()时才会创建实例

单例模式的缺点

1. 违反单一职责原则

// 单例类同时负责创建实例和管理业务逻辑
public class Singleton {private static Singleton instance;// 业务方法public void businessMethod() { /* ... */ }// 单例管理方法public static Singleton getInstance() { /* ... */ }
}

2. 难以测试

// 由于是全局状态,单元测试时难以mock
public class UserServiceTest {@Testpublic void testUserService() {// 单例的全局状态可能影响测试结果ConfigurationManager config = ConfigurationManager.getInstance();// 测试可能相互影响}
}

适用场景

  1. 需要频繁实例化然后销毁的对象
  2. 创建对象时耗时过多或耗资源过多,但又经常用到的对象
  3. 有状态的工具类对象
  4. 频繁访问数据库或文件的对象
  5. 资源共享的情况下,避免由于资源操作时导致的性能或损耗

最佳实践

1. 优先使用枚举或静态内部类实现

// 推荐方式1:枚举单例
public enum EnumSingleton {INSTANCE;// 业务方法
}// 推荐方式2:静态内部类
public class InnerClassSingleton {private static class Holder {private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();}public static InnerClassSingleton getInstance() {return Holder.INSTANCE;}
}

2. 考虑线程安全

// 确保在多线程环境下也能正常工作
public class ThreadSafeSingleton {private static volatile ThreadSafeSingleton instance;public static ThreadSafeSingleton getInstance() {if (instance == null) {synchronized (ThreadSafeSingleton.class) {if (instance == null) {instance = new ThreadSafeSingleton();}}}return instance;}
}

3. 防止反射和反序列化破坏

public class ProtectedSingleton {private static final ProtectedSingleton INSTANCE = new ProtectedSingleton();private ProtectedSingleton() {// 防止反射攻击if (INSTANCE != null) {throw new RuntimeException("单例已被创建");}}// 防止反序列化破坏private Object readResolve() {return INSTANCE;}
}

总结

单例模式是设计模式中最简单但也最容易误用的模式之一。正确使用单例模式可以带来很多好处,但滥用也会导致很多问题。

核心价值:

  • 保证一个类只有一个实例
  • 提供全局访问点
  • 控制资源使用

使用建议:

  • 慎重考虑是否真的需要单例
  • 优先使用枚举或静态内部类实现
  • 注意线程安全问题
  • 考虑测试的便利性

选择指南:

  • 如果需要延迟加载:使用静态内部类或双重检查锁
  • 如果需要绝对防止反射攻击:使用枚举
  • 如果实例化开销小且一定会用到:使用饿汉式

掌握单例模式的正确使用方式,能够在合适的场景下大幅提升代码的质量和性能。

http://www.dtcms.com/a/511118.html

相关文章:

  • 第一章 不可变的变量
  • AUTOSAR 中 Trusted Platform(可信平台)详解
  • 2510rs,rust清单2
  • PINN物理信息神经网络股票价格预测模型Matlab实现
  • 2510rs,rust清单3
  • 用ps做网站方法茂名建站模板搭建
  • 怎么建设vip电影网站wordpress轮播图设置
  • docker 更新layer
  • 基于卷积神经网络的香蕉成熟度识别系统,resnet50,vgg16,resnet34【pytorch框架,python代码】
  • 深度学习YOLO实战:6、通过视频案例,解析YOLO模型的能力边界与选型策略
  • C# 识别图片中是否有人
  • [Power BI] 漏斗图(Funnel Chart)
  • 做网站优化响应式网站 企业模版
  • 视觉学习篇——图像存储格式
  • GB28181视频服务wvp搭建(二)
  • Spring Boot安全配置全解析
  • EasyGBS如何通过流媒体技术提升安防监控效率?
  • 做展览的网站国家免费职业培训平台
  • 农业技术网站建设原则曲阜网站建设
  • 【python】基于 生活方式与健康数据预测数据集(Lifestyle and Health Risk Prediction)的可视化练习,附数据集源文件。
  • C#WPF如何实现登录页面跳转
  • 健康与生活方式数据库编程手册(Python方向教学2025年4月)
  • HarmonyOS测试与上架:单元测试、UI测试与App Gallery Connect发布实战
  • 以太网学习理解
  • 微算法科技(NASDAQ MLGO)标准化API驱动多联邦学习系统模型迁移技术
  • 【Redis】三种缓存问题(穿透、击穿、双删)的 Golang 实践
  • 第1部分-并发编程基础与线程模型
  • 【含文档+PPT+源码】基于SSM的智能驾校预约管理系统
  • python股票交易数据管理系统 金融数据 分析可视化 Django框架 爬虫技术 大数据技术 Hadoop spark(源码)✅
  • 有哪些网站可以自己做加视频做一个购物网站