1. 题目
设计一个类,我们只能生成该类的一个实力。
2. 解题思路
这道题也就是我们常说的实现单例模式。
3. 代码实现
3.1 不好的解法一:只适用于单线程
修改类的__new___方法使得这个类只能创建一个实例。但是此解法只适用于单线程。缺点是在多线程情形下,可能多个子线程在判断if cls._instance is None
时都为True,从而有多个子线程创建了多个实例。
class Singleton(object):
_instance = None
def __init__(self, *args, **kwargs):
pass
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
或者自定义一个方法来实现也是可以的
class Singleton(object):
_instance = None
def __init__(self, *args, **kwargs):
pass
def instance(cls, *args, **args):
if cls._instance is None:
cls.instance = Singleton(*args, **kwargs)
3.2 不好的解法二:适用于多线程,但效率不高
虽然此解法在多线程情形下可以正常运行,但是我们每次得到类的实例对象时,都会试图加上一个同步锁
,而加锁是一个非常耗时的操作,在没有必要的时候我们应该尽量避免。
import threading
class Singleton(object):
_instance = None
_lock = threading.Lock()
def __init__(self, *args, **kwargs):
pass
def __new__(cls, *args, **kwargs):
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
3.3 可行的解法
基于3.2中的缺点,对解法二进行了优化。我们只需要在必要的时候再加锁就行了。也就是再实例对象不存在的时候才加锁创建该实例对象,实例对象若存在则不用加锁,直接返回即可。
import threading
class Singleton(object):
_instance = None
_lock = threading.Lock()
def __init__(self, *args, **kwargs):
pass
def __new__(cls, *args, **kwargs):
if cls._instance is None: # 当实例不存在时才加锁
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
4. 总结
在剑指offer一书中,还有利用静态构造函数来实现单例模式,静态构造函数只能执行一次,所以将创建实例的操作放在了静态构造函数中就能只创建一个实例。但是python并没有类似的特性,所以只写了前3种解法。
实现单例模式不是很难,但是需要考虑到在实际业务中,创建实例经常都在多线程环境下进行,那么如何在多线程环境也能实现单例模式才是更好的。
5. 参考文献
[1] 剑指offer丛书
[2] python面试系列之实现单例模式