授课语音

单例模式(Singleton Pattern)

1. 介绍

单例模式是一种设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

单例模式的核心概念

  1. 唯一性:确保一个类只有一个实例。即使多个线程尝试创建该类的对象,也只会存在一个实例。
  2. 全局访问:单例模式提供一个全局访问点来获取这个唯一实例,通常通过静态方法实现,以便在任何地方都可以访问该实例。
  3. 延迟初始化:在一些实现中,单例实例在第一次被请求时才被创建,这样可以避免不必要的开销。这一过程称为延迟初始化(lazy initialization)。

单例模式的变体

  1. 懒汉式单例(Lazy Initialization):在第一次被访问时创建实例,优点是延迟初始化,但需要处理多线程环境下的同步问题。
  2. 饿汉式单例(Eager Initialization):在类加载时就创建实例,这种方式简单且线程安全,但如果实例创建的开销较大而应用从未使用该实例,则可能会造成资源浪费。
  3. 双重检查锁(Double-Check Locking):在多线程环境下,通过双重检查和锁机制来确保实例创建的线程安全性。通常结合volatile关键字使用以避免指令重排序带来的问题。
  4. 登记式单例(Registry-based Singleton):使用注册表管理单例实例。通过静态方法或其他机制从注册表中获取单例实例,适合需要支持动态实例管理的场景。
  5. 枚举单例(Enum Singleton):使用枚举类型实现单例模式,这是 Java 推荐的实现方式,能够确保线程安全,并防止反序列化攻击。

实际框架中的应用

单例模式在许多实际框架和系统中有广泛的应用。以下是一些常见的应用场景:

  1. 配置管理:许多框架中,配置管理类通常使用单例模式。例如,Django 的 settings 模块就是一个单例,确保配置在整个应用程序中是一致的。

  2. 数据库连接池:数据库连接池通常使用单例模式来管理连接。例如,SQLAlchemy 中的 Engine 对象通常作为单例使用,以确保数据库连接的高效管理。

  3. 日志记录:日志记录器通常使用单例模式来确保日志记录的一致性。例如,Python 的 logging 模块可以配置为单例,以便在整个应用程序中共享同一个日志记录器实例。

  4. 缓存系统:缓存系统如 Redis 或 Memcached 客户端通常使用单例模式,以确保缓存客户端在整个应用程序中只有一个实例,从而提高性能和资源利用率。

  5. Flask 应用:在 Flask 框架中,应用实例通常作为单例使用。Flask 的 app 对象在整个应用程序中是唯一的,确保了路由和配置的一致性。

2. 代码案例

import threading
from enum import Enum


class SingletonExamples:
    # 1. 饿汉式单例(Eager Initialization)
    # 饿汉式单例在模块加载时就创建实例,确保线程安全。
    # 适用于实例创建开销较小且不会造成资源浪费的场景。
    class EagerSingleton:
        _instance = None

        def __new__(cls, *args, **kwargs):
            if cls._instance is None:
                cls._instance = super().__new__(cls, *args, **kwargs)
                cls._instance._initialize()
            return cls._instance

        def _initialize(self):
            # 初始化实例变量
            self.value = "EagerSingleton Value"

        def show_value(self):
            # 显示实例的值
            print(f"EagerSingleton value: {self.value}")

    # 2. 懒汉式单例(Lazy Initialization)
    # 懒汉式单例在第一次使用时创建实例,需要处理线程安全的问题。
    # 使用锁来确保线程安全。
    class LazySingleton:
        _instance = None
        _lock = threading.Lock()

        def __new__(cls, *args, **kwargs):
            if cls._instance is None:
                with cls._lock:  # 确保线程安全
                    if cls._instance is None:
                        cls._instance = super().__new__(cls, *args, **kwargs)
                        cls._instance._initialize()
            return cls._instance

        def _initialize(self):
            # 初始化实例变量
            self.value = "LazySingleton Value"

        def show_value(self):
            # 显示实例的值
            print(f"LazySingleton value: {self.value}")

    # 3. 双重检查锁(Double-Check Locking)
    # 双重检查锁在多线程环境下提供高效的单例实现方式。
    # 使用双重检查锁减少锁的开销。
    class DoubleCheckLockingSingleton:
        _instance = None
        _lock = threading.Lock()

        def __new__(cls, *args, **kwargs):
            if cls._instance is None:
                with cls._lock:  # 确保线程安全
                    if cls._instance is None:
                        cls._instance = super().__new__(cls, *args, **kwargs)
                        cls._instance._initialize()
            return cls._instance

        def _initialize(self):
            # 初始化实例变量
            self.value = "DoubleCheckLockingSingleton Value"

        def show_value(self):
            # 显示实例的值
            print(f"DoubleCheckLockingSingleton value: {self.value}")

    # 4. 登记式单例(Registry-based Singleton)
    # 登记式单例使用一个注册表来管理单例实例。
    # 适用于动态管理单例的场景。
    class RegistrySingleton:
        _registry = {}

        def __new__(cls, key, *args, **kwargs):
            if key not in cls._registry:
                cls._registry[key] = super().__new__(cls, *args, **kwargs)
                cls._registry[key]._initialize()
            return cls._registry[key]

        def _initialize(self):
            # 初始化实例变量
            self.value = "RegistrySingleton Value"

        def show_value(self):
            # 显示实例的值
            print(f"RegistrySingleton value: {self.value}")

    # 5. 枚举单例(Enum Singleton)
    # 枚举单例是Python推荐的实现单例模式的方式,能够防止反序列化攻击,并确保线程安全。
    class EnumSingleton(Enum):
        # 定义唯一的枚举成员 INSTANCE,用于存储单例实例
        INSTANCE = 1

        def show_value(self):
            # 打印实例变量的值
            print(f"EnumSingleton value: {self.name}")


# 示例代码
if __name__ == "__main__":
    # 1. 饿汉式单例
    eager1 = SingletonExamples.EagerSingleton()
    eager2 = SingletonExamples.EagerSingleton()
    eager1.show_value()
    print(f"Are both EagerSingleton instances the same? {eager1 is eager2}")

    # 2. 懒汉式单例
    lazy1 = SingletonExamples.LazySingleton()
    lazy2 = SingletonExamples.LazySingleton()
    lazy1.show_value()
    print(f"Are both LazySingleton instances the same? {lazy1 is lazy2}")

    # 3. 双重检查锁
    double_check1 = SingletonExamples.DoubleCheckLockingSingleton()
    double_check2 = SingletonExamples.DoubleCheckLockingSingleton()
    double_check1.show_value()
    print(f"Are both DoubleCheckLockingSingleton instances the same? {double_check1 is double_check2}")

    # 4. 登记式单例
    registry1 = SingletonExamples.RegistrySingleton("key1")
    registry2 = SingletonExamples.RegistrySingleton("key1")
    registry1.show_value()
    print(f"Are both RegistrySingleton instances the same? {registry1 is registry2}")

    # 5. 枚举单例
    enum_singleton = SingletonExamples.EnumSingleton.INSTANCE
    enum_singleton.show_value()

详细中文注释

  • 类说明:每个单例实现类都有一个内部初始化方法,用于设置初始值。在每个 show_value 方法中打印该实例的值,以验证实例的唯一性。
  • 示例代码:在主程序中,我们创建每种单例的两个实例并检查它们是否相同,以证明单例模式的有效性。每个实例调用 show_value 方法显示其值。
去1:1私密咨询

系列课程: