设计模式--单例模式

单例模式(Singleton ):保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  • 实现方法:通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。解决办法就是,让类自身负责保存它的唯一实例。这个类保证没有其他实例可以创建,并提供一个访问该实例的方法。(来自《大话设计模式》)
  • 单例的两种实现:
    • 懒汉模式:第一次引用时才会被实例化。
    • 饿汉模式:自己被加载时就将自己实例化。

多线程时的单例-懒汉模式

为了保证多线程的安全,要采用双重锁定。在VS 2013 IDE中

std::mutex mtx;

class Singleton
{
public:
	static Singleton* GetInstance(); //公有静态方法获取实例

private:
	static Singleton *ptr; //私有静态指针变量指向唯一实例
	Singleton(){} // 构造函数私有
};

Singleton* Singleton::ptr = nullptr;

Singleton* Singleton::GetInstance(){
	if (ptr == nullptr){
		mtx.lock();
		if (ptr == nullptr){
			ptr = new Singleton;
		}
		mtx.unlock();
	}
	return ptr;
}

为什么要判断两次 ptr 实例是否存在呢?就是在mtx.lock()外面已经判断了ptr实例是否存在,为什么在mtx.lock()里面还要再判断一次实例是否存在呢?
设计模式--单例模式_第1张图片
如果实例已经存在了,那么直接返回,没有问题。但是如果实例不存在,并且有两个线程(thread1,thread2)同时调用 GetInstance() 方法,两个都可以通过第一个 ptr == nullptr 判断。然后由于lock机制,只有一个线程能够进入,另外一个排队等候,必须要其中的一个进入并出来后,另一个才能进入。这时如果没有第二个 ptr == nullptr 判断,则第一个线程创建了实例,第二个线程同样可以创建新的实例,就没有达到单例的目的了。

局部静态变量版的线程安全懒汉模式

在《Effective C++》的条款04:确定对象被使用前已先被初始化提出了更优雅的单例模式,使用函数内的局部静态变量,这种方法不用加锁解锁。

std::mutex mtx;
class Singleton
{
public:
	static Singleton* GetInstance(){
        static Singleton instance;
		return &instance;
    }

private:
	Singleton(){}
	~Singleton(){}
};

使用静态局部变量完全避免手动释放类所分配资源和线程安全问题(c++11新标准支持static局部变量安全问题)

多线程时的单例-饿汉模式

饿汉模式不需要用锁,就可以实现线程安全。原因在于,在程序运行时就定义了静态对象,并对其初始化。之后,不管哪个线程调用成员函数GetInstance(),都只不过是返回一个对象的指针而已。所以是线程安全的,不需要在获取实例的成员函数中加锁。

class Singleton
{
public:
	static Singleton* GetInstance(){
		return ptr;
	}

private:
	static Singleton* ptr;
	Singleton(){}
	~Singleton(){}
};

Singleton* Singleton::ptr = new Singleton;
// test
int main()
{
	Singleton* s1 = Singleton::GetInstance();
	Singleton* s2 = Singleton::GetInstance();
	if (s1 == s2){
		cout << "s1==s2" << endl;
	}
	else
	{
		cout << "s1!=s2" << endl;
	}
	system("pause");
	return 0;
}

你可能感兴趣的:(设计模式,设计模式,单例)