-
目的:确保某一个类只有一个实例存在,并且提供一个访问该实例的全局访问点
-
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可用产生一个"单例对象",然后永久驻留内存中,从而极大的降低开销。
-
产生原因:在面向对象的程序设计中,当业务并发量非常大时,那么就会出现重复创建相同的对象,每创建一个对象就会开辟一块内存空间,而这些对象其实是一模一样的,那么有没有办法使用得内存对象只创建一次,然后再随处使用呢?单例模式就是为了解决这个问题而产生的。
方法一:使用模块
Python的模块是天然的单例模式,因为模块在第一次导入时,会生成.pyc文件,当第二次导入时,会直接加载.pyc文件,而不会再次执行模块代码。因此只需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
单例类:
#!/bin/python
class Singleton(object):
def foo(self):
pass
singleton = Singleton()
使用:
from mysingleton import singleton
print singleton.foo()
方法二:使用装饰器
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class A(object):
a = 1
def __init__(self, x=0):
self.x = x
a1 = A(2)
a2 = A(3)
方法三:使用类
class Singleton(object):
def __init__(self):
#time.sleep(1)
pass
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
import threading
def task(arg):
obj = Singleton.instance()
print (obj)
for i in range(10):
t = threading.Thread(target=task, args=[i,])
t.start()
运行结果:
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
看似没有问题,但如果加上sleep就会发现问题:
<__main__.Singleton object at 0x7fd22612fb50>
<__main__.Singleton object at 0x7fd22612fc90>
<__main__.Singleton object at 0x7fd22612fed0>
<__main__.Singleton object at 0x7fd22613f190>
<__main__.Singleton object at 0x7fd22613f410>
<__main__.Singleton object at 0x7fd22613f690>
<__main__.Singleton object at 0x7fd22613f910>
<__main__.Singleton object at 0x7fd22613fb90>
<__main__.Singleton object at 0x7fd22613fe10>
<__main__.Singleton object at 0x7fd2261470d0>
第一次没有问题是因为执行速度过快。
因此,这种方式不支持多线程。
解决办法:加锁
即:为加锁部分并发执行,加锁部分串行执行,速度降低,但保证了数据安全。
import threading
import time
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
time.sleep(1)
pass
@classmethod
def instance(cls, *args, **kwargs):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
def task(arg):
obj = Singleton.instance()
print (obj)
for i in range(10):
t = threading.Thread(target=task, args=[i,])
t.start()
time.sleep(20)
obj = Singleton.instance()
print obj
执行结果:
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
但还是有不足:
就是当程序执行时,执行了time.sleep(20)后,下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,把intance方法,改成下面的这样就行,如下即为一个完整的支持多线程的单例类。
import time
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
方法四:使用__new__(推荐)
要点:
1. __obj为保存的唯一变量
2. __init_flag是为了保证__init__函数只能调用一次,如果没有这个值,__init__会调用多次。
#!/bin/python
class MySingleton:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self, name):
if MySingleton.__init_flag:
self.name = name
MySingleton.__init_flag = False
a = MySingleton("aa")
b = MySingleton("bb")
c = MySingleton("cc")
print (a)
print (b)
print (c)
运行结果:
[root@localhost ]# python3 new_singleton.py
<main.MySingleton object at 0x7f40e93645f8>
<main.MySingleton object at 0x7f40e93645f8>
<main.MySingleton object at 0x7f40e93645f8>
问题:使用python2运行时地址不同,原因??