定义
单例模式是一种创建型设计模式, 其主要目的是确保一个类只有一个实例, 并提供一个全局访问点来访问该实例。
结构
应用场景
- 资源管理:当需要共享某个资源时,例如数据库连接、线程池、日志对象等,可以使用单例模式确保所有的客户端都使用同一个资源实例,从而避免资源的浪费和不一致性。
- 配置信息:在应用程序中,可能会有一些全局配置信息需要在各个地方被访问和使用,例如系统配置、日志配置等,这时可以使用单例模式来存储和管理这些配置信息。
- 缓存管理:在需要缓存数据以提高性能的场景中,可以使用单例模式来管理缓存实例,确保所有地方都使用同一个缓存对象,避免数据不一致或者缓存混乱的问题。
优缺点
优点:
- 资源节约:单例模式确保一个类只有一个实例存在,可以节约系统资源,避免了多次创建相同类型的对象所带来的资源浪费。
- 全局访问点:单例模式提供了一个全局的访问点,使得可以在任何时候、任何地方都能够访问到该实例,方便了对象的访问和使用。
- 实例控制:由于单例模式只能创建一个实例,因此可以对实例进行严格的控制,例如可以限制实例的数量、延迟实例化等。
缺点:
- 对扩展性的限制:由于单例模式创建的实例是静态的,因此很难对其进行子类化或者扩展。如果需要在单例类的基础上添加新的功能,可能需要修改现有的代码,这会增加耦合性并且破坏了开闭原则。
- 职责过多:单例模式在一定程度上违背了单一职责原则,因为单例类既提供了业务方法,又提供了创建对象的方法,将对象的创建和对象本身的功能耦合在一起。
Java代码示例
饿汉式
饿汉式实现是在类定义时就创建单例对象,不管是否需要使用该对象。
java
public class Singleton {
// 在类加载时就创建好实例
private static Singleton instance = new Singleton();
// 私有化构造函数,防止外部实例化
private Singleton() {}
// 提供一个公共的静态方法返回实例
public static Singleton getInstance() {
return instance;
}
}
懒汉式
懒汉式实现是在需要时才创建实例。
java
public class Singleton {
// 声明一个静态的实例变量,但不初始化
private static Singleton instance;
// 私有化构造函数,防止外部实例化
private Singleton() {}
// 提供一个公共的静态方法返回实例
public static Singleton getInstance() {
// 检查实例是否已经被创建,如果没有,才进行实例化
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
以上实现不是线程安全的,在多线程高并发访问时,要使用双重检查锁定来实现线程安全。
java
public class Singleton {
// 声明一个 volatile 类型的静态变量,确保多线程下的可见性
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;
}
}
静态内部类(推荐)
使用静态内部类实现单例模式是一种常见的方式,这种方法利用了类加载的特性来保证懒加载和线程安全。
java
public class Singleton {
// 私有化构造方法,防止外部实例化
private Singleton() {}
// 静态内部类,用于实现懒加载和线程安全
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供获取单例对象的方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Python代码示例
使用模块
使用模块来实现饿汉式单例模式是一种简单而有效的方法。在 Python 中导入一个模块时,解释器会确保这个模块只被加载一次,因此可以利用这一点来实现单例模式。
python
# singleton.py
class Singleton:
pass
# 创建单例实例
singleton_instance = Singleton()
# main.py
from singleton import singleton_instance
# 使用单例实例
singleton_instance.some_attribute = "some_value"
使用装饰器
使用装饰器可以实现懒汉式单例模式,实例在第一次被请求时才会被创建。
python
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Singleton:
pass
# 使用单例对象
singleton_instance1 = Singleton()
singleton_instance2 = Singleton()
使用类属性
使用类属性也可以实现懒汉式单例模式。
python
class Singleton:
_instance = None # 类属性用于存储单例实例
@classmethod
def get_instance(cls, value):
if not cls._instance:
cls._instance = cls(value)
return cls._instance
# 使用单例对象
singleton_instance1 = Singleton.get_instance()
singleton_instance2 = Singleton.get_instance()
也可以通过 new 方法来实现,同样是将实例对象存储到类属性中。
python
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 使用单例对象
singleton_instance1 = Singleton()
singleton_instance2 = Singleton()
使用元类(推荐)
使用元类可以更加灵活地控制类的创建过程,从而实现懒汉式单例模式。
python
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
pass
# 使用单例对象
singleton_instance1 = Singleton()
singleton_instance2 = Singleton()
在多线程高并发访问时,要使用双重检查锁定来实现线程安全。
python
from threading import Lock, Thread
class SingletonMeta(type):
_instances = {}
_lock = Lock()
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
with cls._lock:
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
pass
饿汉式单例类和懒汉式单例类的比较
- 资源利用率:饿汉式单例类在类被加载时创建实例,无论运行时是否使用该实例;懒汉式单例类在第一次使用时创建实例;所以懒汉式单例类资源利用率更高。
- 线程安全问题:饿汉式单例类在类被加载时创建实例,是线程安全的;懒汉式单例类需要增加双重检查锁定来实现线程安全;
参考
《设计模式的艺术》
单例设计模式 (refactoringguru.cn)
Python中的单例模式的几种实现方式的及优化 - 听风。 - 博客园 (cnblogs.com)