单件模式确保一个类只有一个实例,并提供一个全局访问点。
懒汉模式:
顾名思义,他是一个懒汉,他不愿意动弹。什么时候需要吃饭了,他就什么时候开始想办法搞点食物。
即懒汉式一开始不会实例化,什么时候用就什么时候new,才进行实例化。
说明: 先不创建实例,当第一次被调用时,再创建实例,所以被称为懒汉式。
优点: 延迟了实例化,如果不需要使用该类,就不会被实例化,节约了系统资源。
缺点: 线程不安全,多线程环境下,如果多个线程同时进入了 if (uniquerSingleton== null) ,若此时还未实例化,也就是uniquerSingleton== null,那么就会有多个线程执行 uniquerSingleton= new LazySingleton(); ,就会实例化多个实例;
与之相反的是饿汉式(线程安全)
#include <iostream>
class LazySingleton {
private:
//利用一个静态变量来记录Singleton类的唯一实例
static LazySingleton *uniquerSingleton;
public:
void print() {
printf("Lazy Singleton\n");
}
static LazySingleton* getInstance() {//将构造器声明为私有,只有来自Singleton类内才可以调用构造器
if (uniquerSingleton == nullptr) {
/*如果对象不存在,就利用私有构造器产生 一个实例,
并把它赋值到uniquerSingleton静态变量中,
如果我们不需要,它就永远不会产生,这就是延迟实例化*/
uniquerSingleton = new LazySingleton();
}
return uniquerSingleton;
}
};
LazySingleton* LazySingleton::uniquerSingleton = nullptr;
int main(){
LazySingleton* ls = LazySingleton::getInstance();
ls->print();
return 0;
}
饿汉模式:
顾名思义,他是一个饿汉,他很勤快就怕自己饿着。他总是先把食物准备好,什么时候需要吃了,他随时拿来吃,不需要临时去搞食物。
即饿汉式在一开始类加载的时候就已经实例化,并且创建单例对象,以后只管用即可。
说明: 实现和 线程不安全的懒汉式 几乎一样,唯一不同的点是,在get方法上 加了一把 锁。如此一来,多个线程访问,每次只有拿到锁的的线程能够进入该方法,避免了多线程不安全问题的出现。
优点: 延迟实例化,节约了资源,并且是线程安全的
缺点: 虽然解决了线程安全问题,但是性能降低了。当这个对象特别大的时候,无论你用不用该对象,在使用程序开始的那一课开始,对象已经存在,会造成巨大的内存浪费。
#include <iostream>
class HungrySingleton {
//利用一个静态变量来记录Singleton类的唯一实例
public:
static HungrySingleton* uniquerSingleton;
void print() {
printf("Hungry Singleton\n");
}
static HungrySingleton* getInstance() {//将构造器声明为私有,只有来自Singleton类内才可以调用构造器
return uniquerSingleton;
}
};
//类外进行初始化
HungrySingleton* HungrySingleton::uniquerSingleton = new HungrySingleton;
int main(){
HungrySingleton::uniquerSingleton->print();
return 0;
}
懒汉式和饿汉式的安全和性能区别:
(1) 线程安全:饿汉式在线程还没出现之前就已经实例化了,所以饿汉式一定是线程安全的。懒汉式加载是在使用时才会去new 实例的,那么你去new的时候是一个动态的过程,是放到方法中实现的。
如果这个时候有多个线程访问这个实例,这个时候实例还不存在,还在new,就会进入到方法中,有多少线程就会new出多少个实例。一个方法只能return一个实例,那最终return出哪个呢?是不是会覆盖很多new的实例?这种情况当然也可以解决,那就是加同步锁,避免这种情况发生 。
(2)执行效率:饿汉式没有加任何的锁,因此执行效率比较高。懒汉式一般使用都会加同步锁,效率比饿汉式差。
(3)内存使用:饿汉式在一开始类加载的时候就实例化,无论使用与否,都会实例化,所以会占据空间,浪费内存。懒汉式什么时候用就什么时候实例化,不浪费内存。
双重锁:线程安全的懒汉模式
#include <iostream>
#include <mutex>
std::mutex mt;
class SingleTon {
public:
static SingleTon* getInstance() {
if (m_instance == nullptr) {
mt.lock();
if (m_instance == nullptr) {
m_instance = new SingleTon;
}
mt.unlock();
}
return m_instance;
}
~SingleTon() {
printf("~SingleTon() called!\n");
}
void print() {
if (m_instance == nullptr) {
printf("m_instance == nullptr\n");
}
printf("SingleTon!!!\n");
}
private:
static SingleTon* m_instance;
};
SingleTon* SingleTon::m_instance = nullptr;
int main(int* argc, int* argv[]) {
SingleTon* singleTon = nullptr;
singleTon = SingleTon::getInstance();
singleTon->print();
SingleTon* hehe = nullptr;
hehe = SingleTon::getInstance();
hehe->print();
delete singleTon;
return 0;
}
参考:
1. EricFreeman, FreemanElisabeth, 弗里曼, et al. Head First设计模式[M]. 中国电力出版社, 2007.
837

被折叠的 条评论
为什么被折叠?



