单例模式看wikipedia的解释:
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。
许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
实现单例模式的思路是:
一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。
当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
首先,由于python不强制隐私,访问权限不像Java那样严格执行,所以它没有私有构造函数这一说,全凭你自觉不要访问敏感信息。其次,在python中最接近的构造函数是__new__()
,但这很少使用。通常使用__init__(self)
,由它修改刚刚创建的对象(self代表实例本身)。最后,python既支持函数式编程又支持面向对象编程。
下面介绍两种多线程安全的单例模式python实现。
1. 懒汉式(双重校验锁):
# test.py
import threading
import time
class LazySingleton(object):
# 类变量,__的写法代表这是一个私有变量
# 创建一个线程锁
__lock = threading.Lock()
def __init__(self):
print('初始化了一个类对象')
# 实现阻塞5s中
time.sleep(5)
# 声明一个类方法,相当与java中静态(类)方法
@classmethod
def get_instance(cls, *args, **kwargs):
try:
# 访问类变量__instance,如果不存在会按报错处理,存在就返回
LazySingleton.__instance
return LazySingleton.__instance
except:
# 在执行with块代码的时候加上线程锁,执行完毕释放线程锁
# 此线程锁不是GIL锁
with LazySingleton.__lock:
try:
# 访问类变量__instance,如果不存在会按报错处理,存在就返回
LazySingleton.__instance
return LazySingleton.__instance
except:
# 给LazySingleton类绑定一个变量__instance,相当与Java中的静态(类变变量)
# __的写法代表这是一个私有变量,虽然是私有变量,但是你也可以在类外部访问,原则上你不要访问
LazySingleton.__instance = LazySingleton()
return LazySingleton.__instance
if __name__ == "__main__":
def task():
# 获取LazySingleton类实例/对象
obj = LazySingleton.get_instance()
print(obj)
# 循环创建多个线程
for i in range(10):
t = threading.Thread(target=task)
t.start()
提交执行:
ssh://appl@172.30.1.243:22/opt/appl/anaconda3/bin/python -u /opt/appl/pycharm-projects/spark_streaming_test/test.py
初始化了一个类对象
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
Process finished with exit code 0
2. 装饰器
如果不懂python的函数式编程建议把廖雪峰老师python 函数式编程 这一章看完,不然下面的代码理解会有困难。
python用装饰器实现单例模式思路有些改变了,要单例的类不用做任何改变,不用写获得该实例的方法,和正常类无区别。
# test2.py
import threading
from functools import wraps
import time
class Decorator(object):
# 创建一个线程锁,这是一个类变量,类似于Java中静态变量
__lock = threading.Lock()
__instance = None
# 定义一个高阶函数,cls参数是一个类
# 同时它是一个静态方法,类似于Java中静态(类)方法
@staticmethod
def singleton(cls):
# 定义一个私有方法,wraps作用不知道的自己查,不感兴趣的也不用知道
@wraps(cls)
def __wrapper(*args, **kw):
# 如果类变量__instance不存在就新建,存在就返回
if Decorator.__instance is None:
# 在执行with块代码的时候加上线程锁,执行完毕释放线程锁
# 此线程锁不是GIL锁
with Decorator.__lock:
# 如果类变量__instance不存在就新建,存在就返回
if Decorator.__instance is None:
# 新建一个类
Decorator.__instance = cls(*args, **kw)
return Decorator.__instance
return Decorator.__instance
# 返回值为函数叫做闭包
return __wrapper
# singleton函数是一个装饰器,它接受一个类或函数作为变量,并返回一个函数
# 创建类:DecoratorSingleton()相当于执行了singleton(DecoratorSingleton())
# lock是一个线程锁
@Decorator.singleton
class DecoratorSingleton(object):
# 初始化方法
def __init__(self, name):
print('my name is ' + name)
# 实现阻塞5s
time.sleep(5)
if __name__ == "__main__":
# 创建一个任务函数
def task():
# 获取DecoratorSingleton类实例/对象
obj = DecoratorSingleton('dong')
print(obj)
# 循环创建多个线程
for i in range(10):
t = threading.Thread(target=task)
t.start()
提交执行:
ssh://appl@172.30.1.243:22/opt/appl/anaconda3/bin/python -u /opt/appl/pycharm-projects/spark_streaming_test/test2.py
my name is dong
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
Process finished with exit code 0
利用装饰器实现的单例模式极其灵活,不仅可以用在类上面,还可以用在方法上面,对方法产生的结果进行单例子化。
Python还可以利用重写__new__()
实现单例,有兴趣的同学可以自行研究。