最近在工作中遇到同事在写单例模式…趁机补一下。
单例模式
单例模式属于软件设计模式中创建模式的一种,还有一种著名的创建模式还有工厂模式等。
单例模式(singleton)使得一个类最多只能有一个实例化对象。在很多情形中,某个类虽然可能需要多个实例,但为了方便协调管理系统整体行为、节省计算机的内存资源,希望这些实例本质上是同一个对象。
单例模式的应用举例:
- 服务器配置信息类、打印机显卡驱动等:没有必要创建多份拷贝。
- 线程池、连接池类:对线程、连接的使用由统一的实例控制。
单例模式的优点:
- 内存中最多只有一个对象,节省内存
- 避免对象频繁的创建销毁,提高性能
在一般情况下,单例模式被要求是无状态的。即单例模式的类应当永远处于某一种状态,而不随外界情况发生改变。
单例模式的实现
立即加载(饿汉模式):在类被加载时立即创建实例,此后不再创建新的实例。
优点:实现简单,效率高。
缺点:必然创建一个实例,如果未被使用则浪费资源。
1 | public class Singleton_hungry { |
2 | private static Singleton_hungry singleton_hungry = new Singleton_hungry(); |
3 | private Singleton_hungry(){} |
4 | public static Singleton_hungry getInstance(){ |
5 | return singleton_hungry; |
6 | } |
7 | } |
延迟加载(懒汉模式):将实例化延迟到真正使用的时候。
优点:不会浪费资源。
缺点:需要并发控制,锁的颗粒度大影响性能。
1 | public class Singleton_lazy { |
2 | private static volatile Singleton_lazy lazy = null; |
3 | private Singleton_lazy(){} |
4 | public static synchronized Singleton_lazy getInstance(){ |
5 | if( lazy == null ){ |
6 | lazy = new Singleton_lazy(); |
7 | } |
8 | return lazy; |
9 | } |
10 | } |
双重锁机制(懒汉模式):
优点:进一步减少锁的颗粒度,提升并发性。
此处将锁从getInstance
全局降到了条件判断逻辑中,减少了代码在运行时可能产生的大量不必要的线程锁,提升了并发性。
1 | public class Singleton_DoubleKey { |
2 | private static volatile Singleton_DoubleKey doubleKey = null; |
3 | private Singleton_DoubleKey (){} |
4 | public static Singleton_DoubleKey getInstance(){ |
5 | if( doubleKey == null ){ |
6 | synchronized(Singleton_DoubleKey.class){ |
7 | if( doubleKey == null ){ |
8 | doubleKey = new Singleton_DoubleKey(); |
9 | } |
10 | } |
11 | } |
12 | return doubleKey; |
13 | } |
14 | } |
Python单例模式的实现:
Python单例模式的实现应当使用构造函数__new__
完成,当实例已经存在时拒绝创建新的实例即可,实现较为简单。
1 | import threading |
2 | from random import randint |
3 | |
4 | class Singleton(object): |
5 | _instance_lock = threading.Lock() |
6 | |
7 | def __init__(self): |
8 | print("Singleton __init__ called!") |
9 | # does nothing. |
10 | |
11 | def __new__(cls): |
12 | uid = randint(0, 10000) |
13 | print(f"Singleton __new__ called! id={uid}") |
14 | if not hasattr(Singleton, "_instance"): |
15 | print(f"id={uid} doesn't have attribute '_instance', waiting for _instance_lock.") |
16 | with Singleton._instance_lock: |
17 | print(f"id={uid} gets _instance_lock.") |
18 | if not hasattr(Singleton, "_instance"): |
19 | print(f"id={uid} still doesn't have '_instance', creating instance.") |
20 | Singleton._instance = object.__new__(cls) |
21 | Singleton._asset = "test." |
22 | return Singleton._instance |
23 | |
24 | def __str__(self): |
25 | ret, Singleton._asset = Singleton._asset, "test!" |
26 | return ret |
27 | |
28 | if __name__ == '__main__': |
29 | a = Singleton() |
30 | b = Singleton() |
31 | print(f"id(a)={id(a)}") |
32 | print(f"id(b)={id(b)}") |
33 | print(a) |
34 | print(b) |
35 | |
36 | # Singleton __new__ called! id=3837 |
37 | # id=3837 doesn't have attribute '_instance', waiting for _instance_lock. |
38 | # id=3837 gets _instance_lock. |
39 | # id=3837 still doesn't have '_instance', creating instance. |
40 | # Singleton __init__ called! |
41 | # Singleton __new__ called! id=8947 |
42 | # Singleton __init__ called! |
43 | # id(a)=1482643686848 |
44 | # id(b)=1482643686848 |
45 | # test. |
46 | # test! |
使用时需要注意Python的GIL锁机制。