单例设计模式
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。
在我们一个系统中,什么时候需要用到单例设计模式呢?
例如在一个系统中,我们需要去连接数据库,大家都知道连接数据库是一件非常耗时的事情!
现在我们可以考虑使用连接池的方式来解决这个事情:
- 创建一个类,叫做dbpool
- 在这个类的init函数中我们创建10个数据库连接,并将它们保存到队列中
- 现在隔壁老王想用这个池子,那它就 dbpool(),创建了这个类的实例, 它里面初始化了10个连接
- 一会隔壁老张也想用个池子,那它就dbpool(),创建了这个类的实例, 它里面又重新初始化了10个连接
大家会发现,在我们这个故事中,设计池子这件事很合理,只不过呢使用的时候,每次都重新初始化了池中的10个连接,那怎么让我们这个数据库dbpool只初始化一次呢 ?
接下来,我们首先创建一个单例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Singleton1():
_instance = None
def __new__(cls, *args, **kwargs):
return object.__new__(cls,*args,**kwargs)
def __init__(self):
# 假装做了耗时操作
time.sleep(3)
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = object.__new__(cls);
# 返回类的实例
return cls._instance;
|
这个类,我们直接调用是没有任何问题的,但是如果我们使用多线程去访问它,那就有问题啦!
我们可以来测试一下这个类, 你会发现它还是出现了创建了多个实例
| import unittest
class TestSingleton(unittest.TestCase):
def testSingleton1(self):
for i in range(20):
threading.Thread(target=lambda :print(Singleton1())).start()
|
问题其实就出在了__init__中做了耗时的操作,接下来,我们给大家一种解决方案!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class Singleton():
_instance_lock = threading.Lock();
def __init__(self):
print("init")
time.sleep(5)
def __new__(cls, *args, **kwargs):
if not hasattr(cls,"_instance"):
with cls._instance_lock:
if not hasattr(cls,"_instance"):
cls._instance = object.__new__(cls,*args,**kwargs)
return cls._instance;
|
我们再使用前面的套路来测试一下它:
| def testSingleton2(self):
for i in range(20):
threading.Thread(target=lambda :print(Singleton())).start()
|
至此,我们这个Singleton类就完成啦! 无论调用多少次,其实最终都只返回一个实例!