在阅读《Effective C++》时,接触到了单例模式,而对于其所包含的懒汉模式和饿汉模式,现做如下笔记:
1 单例模式
首先要知道单例模式是什么。单例模式是一种设计模式,就是**保证一个类仅有一个实例,并提供一个访问它的全局访问点。**而懒汉模式和饿汉模式的区别则在于,该实例的创建时机不同,见如下。
2 懒汉模式
顾名思义,该模式很“懒”,所以实例要在迫不得已的情况下才被创建,即需要使用到该实例的时候才创建出来。如下有个懒汉的小demo:
class Singleton{
static Singleton* ins;
Singleton(){}
Singleton* getInstance(){
if (ins == nullptr){
ins = new Singleton();
}
return ins;
}
};
static Singleton* Singleton::ins = nullptr; // 静态成员变量需要在类外定义
3 饿汉模式
饿汉则是,在第一次定义这个单例类的时候就要进行实例化,这样之后就可以随取随用,看这个demo:
class Singleton{
static Singleton* ins = new Singleton();
Singleton(){}
Singleton* getInstance(){
return ins;
}
};
4 懒汉模式和饿汉模式的安全性对比
首先,饿汉模式是具备线程安全性的。这是因为饿汉在线程还没有创建的时候,就已经实例化出来了,所以保证了线程安全性。
而对于懒汉模式,由于是在使用的时候,实例才会被创建出来,所以如果当前环境下有多个线程时,当这些线程需要用到该实例时,就会调用getInstance
方法,而这个实例是静态变量,只会存在一个,那么那么多个线程都去创建这个实例的话,就会出现覆盖结果,导致最后return的这个实例不知道是哪个线程创建的。所以为了解决这个问题,就需要用到同步锁,用互斥锁即可解决。在多线程环境下,互斥锁以全局变量或者静态变量定义,这样每个线程都能使用到这个锁。那么懒汉模式的demo可改为如下:
class Singleton{
static Singleton* ins;
static pthread_mutex_t mutex;
Singleton(){}
Singleton* getInstance(){
if (ins == nullptr){
pthread_mutex_lock(&mutex);
if (ins == nullptr){
ins = new Singleton();
}
pthread_mutex_unlock(&mutex);
}
return ins;
}
};
// 静态成员变量需要类外定义
static Singleton* Singleton::ins = nullptr;
static pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER; // mutex可以这样初始化
这样,懒汉模式也能保证线程安全了。
5 懒汉模式与饿汉模式的效率对比
- 从执行效率上来说,因为懒汉模式会有加锁、解锁等额外操作,所以效率会比饿汉模式低;
- 从内存使用上来说,因为饿汉模式在定义类的时候,就会实例化,所以实例会一直占用着内存,而懒汉模式是在使用的时候才会实例化,因此懒汉模式的内存使用效率上会更高。
6 单例模式的应用场景
- 当系统只需要一个实例对象时,或者考虑到资源消耗太大而只允许创建一个对象的时候,可以使用单例模式;
- 当客户调用类的单个实例,而只允许使用一个公共访问点(除了该访问点之外不允许通过其他方式来访问该实例)时,可以使用单例模式。
参考文章:
(47条消息) 单例模式中的懒汉模式和饿汉模式是什么?区别又是什么?_因为我的心的博客-优快云博客_懒汉模式和饿汉模式区别