跳转到内容

单例模式 (Singleton Pattern)

来自代码酷
Admin留言 | 贡献2025年5月12日 (一) 00:28的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

单例模式 (Singleton Pattern)[编辑 | 编辑源代码]

单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这种模式常用于需要控制资源访问的场景,如数据库连接、日志记录器或配置管理器。

设计意图[编辑 | 编辑源代码]

单例模式的核心目标是:

  • 保证一个类仅有一个实例
  • 提供该实例的全局访问点
  • 控制实例化过程(通常延迟初始化)

实现方式[编辑 | 编辑源代码]

基础实现(非线程安全)[编辑 | 编辑源代码]

最简单的单例实现方式如下,但仅适用于单线程环境:

public class Singleton {
    private static Singleton instance;
    
    // 私有构造函数防止外部实例化
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

问题:多线程环境下可能创建多个实例。

线程安全实现[编辑 | 编辑源代码]

方法1:同步方法[编辑 | 编辑源代码]

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

缺点:每次获取实例都需要同步,性能较差。

方法2:双重检查锁定[编辑 | 编辑源代码]

更高效的线程安全实现:

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

关键点

  • volatile 防止指令重排序
  • 双重检查减少同步开销

方法3:静态内部类[编辑 | 编辑源代码]

利用类加载机制保证线程安全:

public class Singleton {
    private Singleton() {}
    
    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

优点:线程安全且实现简洁,延迟初始化。

方法4:枚举实现[编辑 | 编辑源代码]

Java最佳实践方式:

public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        // 实例方法
    }
}

优势

  • 绝对防止多次实例化
  • 自动支持序列化
  • 线程安全

类图[编辑 | 编辑源代码]

classDiagram class Singleton { -static instance: Singleton -Singleton() +getInstance() Singleton }

实际应用场景[编辑 | 编辑源代码]

案例1:数据库连接池[编辑 | 编辑源代码]

大多数应用只需要一个数据库连接池实例来管理所有数据库连接:

public class DatabasePool {
    private static DatabasePool instance;
    private List<Connection> connections;
    
    private DatabasePool() {
        // 初始化连接池
    }
    
    public static synchronized DatabasePool getInstance() {
        if (instance == null) {
            instance = new DatabasePool();
        }
        return instance;
    }
    
    public Connection getConnection() {
        // 获取连接逻辑
    }
}

案例2:应用配置管理[编辑 | 编辑源代码]

全局配置只需要加载一次:

class AppConfig:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.load_config()
        return cls._instance
    
    def load_config(self):
        # 从文件加载配置
        pass

优缺点分析[编辑 | 编辑源代码]

优点 缺点
控制实例数量,节约系统资源 违反单一职责原则(同时控制实例化和业务逻辑)
全局访问点方便管理 难以扩展(通常不允许子类化)
延迟初始化优化性能 多线程环境需要特殊处理
避免全局变量污染命名空间 单元测试困难(难以模拟替换)

数学原理[编辑 | 编辑源代码]

单例模式可以看作是对集合基数的约束:

|S|1

其中S是实例集合。当实现完全正确时:

|S|=1

常见问题[编辑 | 编辑源代码]

如何防止反射攻击?[编辑 | 编辑源代码]

在构造函数中添加检查:

private Singleton() {
    if (instance != null) {
        throw new IllegalStateException("Already initialized");
    }
}

如何防止反序列化创建新实例?[编辑 | 编辑源代码]

实现readResolve()方法:

protected Object readResolve() {
    return getInstance();
}

何时应该使用单例?[编辑 | 编辑源代码]

  • 当系统需要且只需要一个实例时
  • 当需要严格控制全局访问点时
  • 当实例需要延迟初始化时

替代方案[编辑 | 编辑源代码]

对于需要灵活控制实例数量的场景,可以考虑:

  • 依赖注入容器
  • 静态工具类(无状态时)
  • 工厂模式控制实例化

总结[编辑 | 编辑源代码]

单例模式是设计模式中最简单但实现最微妙的一种。正确实现需要考虑线程安全、序列化、反射等问题。在Java中,枚举实现是最安全的方式;其他语言也都有各自的实现最佳实践。使用时需权衡其便利性与对代码可测试性的影响。