单例模式:
就是确保一个类只有一个实例.当你希望整个系统中,某个类只有一个实例时,单例模式就派上了用场。单例模式能够保证调用者读到一致的信息。
另外,我将单例模式又分为了两种类型:
初始化动作只执行一次:适用于初始化参数固定的场景。如初始化项目配置文件。
每次都初始化:适用于初始化参数随调用者的不同而变化的场景,注意每次初始化都会影响所有调用者读取到的信息。
实例:
某个服务器的配置信息存在在一个文件中,客户端通过AppConfig类来读取配置文件的信息.如果程序的运行的过程中,很多地方都会用到配置文件信息,则就需要创建很多的AppConfig实例,这样就导致内存中有很多AppConfig对象的实例,造成资源的浪费.其实这个时候AppConfig我们希望它只有一份,就可以使用单例模式.
代码实现:
只介绍一种基于__new__方法实现的单例模式(推荐使用,方便)。
此方式每次调用对象,__init__方法都会重新执行,虽然都是一个实例对象。
而采用元类方式实现的单例模式。__init__只有第一次调用对象时执行。
import threading, time
class Singleton(object):
def __init__(self, *args, **kwargs):
time.sleep(1)
pass
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super().__new__(cls)
return cls._instance
def task(arg):
obj = Singleton()
print(obj)
for i in range(10):
t = threading.Thread(target=task, args=[i, ])
t.start()
<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>
问题出现了!按照以上方式创建的单例,无法支持多线程,因为最先执行的线程卡在init中,还没有实例化出_instance。所以其他线程依然可以正常实例化对象。
如何解决呢? 加锁:
import threading, time
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self, *args, **kwargs):
time.sleep(1)
pass
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
with cls._instance_lock:
if not hasattr(cls, '_instance'):
cls._instance = super().__new__(cls)
return cls._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1, obj2)
def task(arg):
obj = Singleton()
print(obj)
for i in range(10):
t = threading.Thread(target=task, args=[i, ])
t.start()
博主实际开发中的应用:利用第三方SDK发短信功能、简历敏感信息屏蔽功能,offer生成功能。