0%

对单例模式的理解

最近在工作中遇到同事在写单例模式…趁机补一下。

单例模式

单例模式属于软件设计模式中创建模式的一种,还有一种著名的创建模式还有工厂模式等。

单例模式(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锁机制。