设计模式系列-单例模式
顾名思义,单例模式(Singleton Pattern)就是保证一个类有且仅有一个实例,并且提供了一个全局的访问点。这就要求我们绕过常规的构造器,提供一种机制来保证一个类只有一个实例,客户程序在调用某一个类时,它是不会考虑这个类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者的责任。
举例:一个应用程序的日志,应用程序的多处都要调用日志进行记录,因此保证类只有一个实例可以将日志内容记录到统一的文件内,防止多个日志实例对象同时写入该文件导致乱序、访问错误等问题。
实现方法-JAVA
1.懒汉式(线程不安全)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Logger {
private static Logger uniqueInstance;
private Logger(String output_file) {
// 初始化日志系统,包括日志文件路径、格式设置等
// ......
}
public static Logger getUniqueInstance(String output_file) {
if (uniqueInstance == null) {
uniqueInstance = new Logger(output_file);
}
return uniqueInstance;
}
}
说明: 先不创建实例,当第一次被调用时,再创建实例,所以被称为懒汉式。 优点: 延迟了实例化,如果不需要使用该类,就不会被实例化,节约了系统资源。 缺点: 线程不安全,多线程环境下,如果多个线程同时进入了 if (uniqueInstance == null) ,若此时还未实例化,也就是uniqueInstance == null,那么就会有多个线程执行 uniqueInstance = new Singleton(); ,就会实例化多个实例;
2.饿汉式(线程安全)
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Logger {
private static output_file = "xxx";
private static Logger uniqueInstance = new Logger(output_file);
private Logger(String output_file) {
// 初始化日志系统,包括日志文件路径、格式设置等
// ......
}
public static Logger getUniqueInstance() {
return uniqueInstance;
}
}
说明: 先不管需不需要使用这个实例,直接先实例化好实例 (饿死鬼一样,所以称为饿汉式),然后当需要使用的时候,直接调方法就可以使用了。 优点: 提前实例化好了一个实例,避免了线程不安全问题的出现。 缺点: 直接实例化好了实例,不再延迟实例化;若系统没有使用这个实例,或者系统运行很久之后才需要使用这个实例,都会操作系统的资源浪费。
3.懒汉式(线程安全)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Logger {
private static Logger uniqueInstance;
private Logger(String output_file) {
// 初始化日志系统,包括日志文件路径、格式设置等
// ......
}
public static synchronized Logger getUniqueInstance(String output_file) {
if (uniqueInstance == null) {
uniqueInstance = new Logger(output_file);
}
return uniqueInstance;
}
}
说明: 实现和线程不安全的懒汉式几乎一样,唯一不同的点是,在get方法上加了一把锁。如此一来,多个线程访问,每次只有拿到锁的的线程能够进入该方法,避免了多线程不安全问题的出现。 优点: 延迟实例化,节约了资源,并且是线程安全的。 缺点: 虽然解决了线程安全问题,但是性能降低了。因为,即使实例已经实例化了,既后续不会再出现线程安全问题了,但是锁还在,每次还是只能拿到锁的线程进入该方法,会使线程阻塞,等待时间过长。
其余方法
双重检查锁实现(线程安全) 静态内部类实现(线程安全) 枚举类实现(线程安全) 本人主要语言不是java因此不深入介绍了
实现方法-Python
1.函数装饰器
使用_instance字典,不可变的类地址作为键、其实例作为值,记录已经创建的类实例。如果一个类已经创建了实例,则返回该实例,实现了单例模式。 实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def singleton(loggger):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class Logger(object):
def __init__(self):
pass
log1 = Logger()
log2 = Logger()
print(id(log1) == id(log2))
# 输出结果:
# True
内置函数id,返回对象的“标识值”。该值是一个整数,在此对象的生命周期中保证是唯一且恒定的。输出结果为True表明,log1和log2指向了同一个对象。
2.类装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton(object):
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls]
@Singleton
class Logger2(object):
def __init__(self):
pass
log1 = Logger2()
log2 = Logger2()
print(id(log1) == id(log2))
和函数装饰器实现同理
3.new关键字实现单例模式
使用 new 方法在创造实例时进行干预,达到实现单例模式的目的:
1
2
3
4
5
6
7
8
9
10
11
12
class Single(object):
_instance = None
def __new__(cls, *args, **kw):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kw)
return cls._instance
def __init__(self):
pass
single1 = Single()
single2 = Single()
print(id(single1) == id(single2))
此处使用_instance来存放一个实例
4.metaclass元类
#todo
参考: java单例模式的六种实现 https://juejin.cn/post/6844904121837830151 Python单例模式(Singleton)的N种实现 https://zhuanlan.zhihu.com/p/37534850