单例模式的几种实现

首先说明:“singleton”是一个单词,不是一个复合词。

接下来步入正题。对于单例模式无论是“饿汉”还是“懒汉”都必须要有的是:

一.私有构造函数

二.声明静态单例对象

如果是“懒汉”的话为了写出好的没有 bug的单例代码还应注意

三.构造单例对象之前要加锁(lock一个静态的object对象)

四.需要两次检测单例实例是否已经被构造,分别在锁之前和锁之后

1、常规单例模式实现:

/*方法一:实现Singleton模式常规方法*/
class Singleton{
private:
	Singleton() { }              //关键点0:构造函数私有
	static Singleton *p;         //关键点1:声明单例对象是静态的      
public:
	static Singleton * instance();
};
Singleton * Singleton::instance() {   //通过静态方法构造对象
	if (p == NULL)                //关键点2:判断单例对象是否被构造
		p = new Singleton();
	return p;
}
Singleton * Singleton::p=NULL; //注意!

分析:这段代码在通常情况下是可以运行的。但是遇到多线程可能就不行了。考虑两个线程同时调用instance方法且同时检测到pNULL,显然此时就不是单例了。

既然说到了多线程,显然能想到加锁来处理。于是有:

2、常规单例模式加锁:

//方法二:在常规常规方法基础上加锁
class Singleton {
private:
	Singleton() {                     //关键点0:构造函数私有,对锁初始化。
		pthread_mutex_init(&mutex,NULL);
	}                
	static Singleton *p;              //关键点1:声明单例对象是静态的
public:
	static pthread_mutex_t mutex;     //创建一个互斥锁
	static Singleton *instance();
};

Singleton *Singleton::instance() {
	if (p == NULL) {                  //关键点2:判断单例对象是否被构造
		pthread_mutex_lock(&mutex);   //关键点3:加线程锁
		p = new Singleton;
		pthread_mutex_unlock(&mutex); //解锁
	}
	return p;
}
Singleton* Singleton::p = NULL;
pthread_mutex_t Singleton::mutex;

分析:由于延迟加载或者缓存的原因,一次判断仍然不能保证只创建一个单例。于是乎有下面双重检查锁定模式。

3、双重检查锁定:

//方法三:双重检查锁定模式
class Singleton {
private:
	Singleton() {                     //关键点0:构造函数私有(并对锁初始化)。
		pthread_mutex_init(&mutex, NULL);
	}
	static Singleton *p;              //关键点1:声明单例对象是静态的
public:
	static pthread_mutex_t mutex;     //创建一个互斥锁
	static Singleton *instance();
};

Singleton *Singleton::instance() {
	if (p == NULL) {                  //关键点2:判断单例对象是否被构造
		pthread_mutex_lock(&mutex);   //关键点3:加线程锁
		if (p == NULL) {              //关键点4:二次判断是否已经初始化
			p = new Singleton;
		}
		pthread_mutex_unlock(&mutex); //解锁
	}
	return p;
}
Singleton* Singleton::p = NULL;
pthread_mutex_t Singleton::mutex;

分析:至此代码就能确保单例,是可以接受的。但是这样的代码实现起来比较复杂,容易出错。

于是引出了“懒汉模式”和“饿汉模式”。

4、“懒汉模式”和“饿汉模式

懒汉:万不得已才实例化类。延时加载——以时间换空间。

饿汉:一上来就把对象给new出来。非延时加载——以空间换时间

显然上面的代码都是“懒汉模式”,那嫩否给出一个“饿汉模式”的例子呢?当然

5、饿汉模式


//方法4:饿汉模式_这样所也不用加了。
/*人家还没要,自己就忍不住的先准备好了,如饥似渴,所以叫饿汉模式*/
class Singleton {
private:
	Singleton() {}                     //关键点0:构造函数私有。          
	static Singleton* p;               //关键点1:声明静态单例对象是
public:
	static Singleton* instance() {     //关键点3:需要的时候直接返回就好了。
		return p;
	}
};
Singleton* Singleton::p=new Singleton(); //关键点2:直接先创建了。

分析:代码非常短,效果非常好,连锁都不用了。

其实只要记住后面两个实现就可以了。如果更注重空间性能就选择“双重检查锁定”,如果更注重时间性能则果断选择“饿汉模式”。

https://www.jb51.net/article/100585.htm

http://blog.jobbole.com/109449/

https://www.cnblogs.com/dupengcheng/p/7205527.html






猜你喜欢

转载自blog.csdn.net/mijichui2153/article/details/80986653