单例模式
动机
- 一个特殊的类,保证在系统中只存在有一个实例。
- 绕过常规的构造器,提供一种机制来保证一个类只有一个实例
定义
保证一个类只有一个实例,并提供一个该实例的全局访问点
代码实现
- Cpp
类的定义
class Singleton{
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static Singleton* m_instance;
};
Singleton* Singleton::m_instance=nullptr;
线程非安全版本
Singleton* Singleton::getInstance() {
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
线程安全 但是锁代价过高的版本
//线程安全版本,但锁的代价过高
Singleton* Singleton::getInstance() {
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
双检查锁,由于内存读写指令重排序不安全
在 m_instance = new Singleton()
中,指令流分为三部分
1.分配内存
2.调用构造器,将内存初始化
3.将内存地址赋值给 m_instance
但是在 CPU 级别,可能有指令重排序,可能顺序为
1.分配内存
2.将内存地址赋值给 m_instance
3.调用构造器,初始化内存
在重排序的顺序中,如果 TreadA 执行到了 m_instance = new Singleton()
的第二步骤
此时 m_instance
不为 nullptr
如果此时有另一个线程执行了 if(m_instance==nullptr)
此判断,则认为m_instance
可以使用,其实不能使用
Singleton* Singleton::getInstance() {
if(m_instance==nullptr){
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
}
return m_instance;
}
- Java
public final class Singleton {
// The field must be declared volatile so that double check lock would work
// correctly.
// 必须使用 volatile 关键字修饰来避免指令重排序
private static volatile Singleton instance;
public String value;
private Singleton(String value) {
this.value = value;
}
public static Singleton getInstance(String value) {
if(instance == null){
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton(value);
}
}
}
return instance;
}
}
单例模式,还有一个问题,是反序列化导致的非单例;
如果是 java 的话,一般就需要重写 readObject 方法的判断逻辑, python 不太清楚。。