单例模式
什么是单例模式呢?
在项目中,有些类是需要对它们进行“计划生育”的,即这个类只能有一个实例,如果出现多个实例则会有数据不一致的风险。
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
这个模式的类,叫单例类,所以类图我就不画了吧。
单例模式的应用场景举例:牵扯到数据问题,数据库首当其冲,缓存自然也跑不了。
代码实现
//这里是.h文件
//老板类单例
class Single_Boss
{
public:
static Single_Boss *instence();//获取数据库单例
//重点在这个函数
void run();
private:
Single_Boss();
~Single_Boss();
char *errmsg;
static Single_Boss *Boss;//实例
};
//源文件
Single_Boss *Single_Boss::Boss= NULL;
Single_Boss::Single_Boss()
{
cout << "Big Boss" << endl; //debug
}
Single_Boss::~Single_Boss()
{
cout<<"Seeyou Boss"<<endl;
}
Single_Boss* Single_Boss::instence()
{
if(!Boss)
{
Boss= new Single_Boss();
}
return Boss;
}
void Single_Boss::run(){
cout<<"你好,欢迎光临XXX,请问需要点什么服务?"<<endl;
}
int main()
{
//Single_Boss *boss = new Single_Boss(); //不信的话大可以将这一行放出来,下面那行屏蔽掉试试
Single_Boss *boss= Single_Boss::instence(); //这是在类外使用单例
boss->run();
return 0;
}
提升部分
多线程下的单例模式
曾经有一份真挚的数据库摆在我眼前,可惜我没有去珍惜它,直到我的项目屡屡崩溃,我才知道,如果能重来,我要加个锁。。。
俱往矣,数风流人物,还看今朝。
来我们重新审视一下下面这段代码:
Single_Boss* Single_Boss::instence() //1
{
if(!Boss) //2
{
Boss= new Single_Boss(); //3
}
return Boss;
}
如果在多线程情况下,一旦有两个线程同时进入了 2 ,怎么办?这不是十分正常的事情吗?一点防范都没有,这不是送人头的行为吗?
白给!!
所以,改一下:
Single_Boss* Single_Boss::instence() //1
{
lock(db_mutex); //假设这个锁我已经初始化过了
if(!Boss) //2
{
Boss= new Single_Boss(); //3
}
unlock(db_mutex); //上锁和解锁一定要同时写,就算忘记写中间步骤,也要先写解锁
return Boss;
}
这样写,可还行?
当然可以。
也可以这样再加上一层,会好一些:
Single_Boss* Single_Boss::instence() //1
{
if(!Boss){
//一重锁定
lock(db_mutex);
if(!Boss) //二重锁定
{
Boss= new Single_Boss();
}
unlock(db_mutex); //上锁和解锁一定要同时写,就算忘记写中间步骤,也要先写解锁
}
return Boss;
}
这个是“懒汉式单例”
饿汉式单例
什么是饿汉式呢?饿汉模式的关键:初始化即实例化
微调上面的代码:
Single_Boss *Single_Boss::Boss= new Single_Boss();
Single_Boss* Single_Boss::instence()
{
//不需要进行实例化
//if(!Boss)
//{
// Boss= new Single_Boss();
//}
return Boss;
}
一般饿汉式加载所导致的弊端是可能我并不想使用实例但是实例已经被构造,相对于懒汉式的用则构造会造成内存的浪费,但是其实现方式很简单。
懒汉还是饿汉?
选哪个可以看个人喜好吧,这里给出一点建议:
懒汉:在访问量较小时,采用懒汉实现。这是以时间换空间。
饿汉:由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
单例模式的优缺点
优点
由于单例模式在内存中只存在一个对象,减少了内存的开支,特别是当对象需要频繁的创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
单例模式可以避免对内存的多重占用。
单例模式可以在系统设置全局的访问点,优化和共享资源访问。这招我经常用,也很喜欢,因为确实方便,做一个标志位单例类,负责所有数据表的映射处理。(要了解可以私信我)
缺点
单例模式一般没有接口,难以拓展。如果要拓展,考虑重构。
单例模式对于测试是不利的。在并发环境中,如果单例没有完成,是不能进行测试的。