单例模式(懒汉单例和饿汉单例)
转载于:http://www.manongjc.com/article/53899.html
==单例模式就是指一个类在整个程序中只有一个实例。==确保一些不需要重复创建的类创建多余的实例。特别是某些工具类,在所有地方使用该类都只需要一个实例。
基本思路就是在单例类内部创建一个静态的自身对象,并自己管理自己。
下面代码用锤子Hammer来表示这个工具,简单的实现如下
1、懒汉模式
懒汉模式就是指铁匠是个懒汉,这个锤子类的实例在没人用的时候,铁匠不去造它,这样做能节约铁匠铺的空间。
#include <QCoreApplication>
#include <QDebug>
///NOTE7单例模式--懒汉单例的线程不安全做法
class Hammer
{private:static Hammer* m_pHammer;Hammer() {qDebug() << "make a new hammer!";}~Hammer() {}Hammer(const Hammer&) = delete;//单例类建议屏蔽拷贝构造函数Hammer &operator =(const Hammer&rhs) = delete;//单例类建议屏蔽拷贝运算符
public:static Hammer* GetHammer(){if(m_pHammer == nullptr){qDebug() << "make a new hammer!";//由于静态变量的资源在程序最后全部统一释放,所以这个new没有对应的delete问题不大,严谨的话也可以加上释放过程m_pHammer = new Hammer();return m_pHammer;}}void Beat(){qDebug() << "I can beat";}
};Hammer* Hammer::m_pHammer = nullptr;int main(int, char **)
{Hammer::GetHammer()->Beat();//获取锤子进行敲打getchar();return 0;
}
上述单例是存在线程安全问题的,比如有两个客人(多线程)同时需要使用锤子,两个客人的访问流程同时走到了锤子是否被实例化那一个if语句中,此时条件都成立,因此就会产生两把不一样的锤子供客人使用,这明显是不符合设计初衷的。可以通过线程操作的一些方法解决,下面再说。
2、饿汉模式
这个模式可以较简单地解决同时有客人光顾的问题。即铁匠是个饿汉,总是担心生意上门了却没锤子可提供,因此铁匠铺开张的时候(类定义时)就把锤子造好等人来用。代码如下:
//饿汉单例
class Hammer
{private:static Hammer* m_pHammer;Hammer(){qDebug() << "make a new hammer!";}~Hammer() {}Hammer(const Hammer&) = delete;//单例类建议屏蔽拷贝构造函数Hammer &operator =(const Hammer&rhs) = delete;//单例类建议屏蔽拷贝运算符
public:static Hammer* GetHammer(){return m_pHammer;//客人访问时直接提供生产好的锤子}void Beat(){qDebug() << "I can beat";}
};
//先将锤子生产好
Hammer* Hammer::m_pHammer = new Hammer();
int main(int, char **)
{Hammer::GetHammer()->Beat();//获取锤子进行敲打getchar();return 0;
}
可以看出,和懒汉模式唯一的区别就在于是否先生产好锤子。这种方式会使得空间占用变多,如果没人使用就白白浪费了。
3懒汉模式的改进
懒汉模式可以采用线程锁的方式进行改进,在铁匠铺装一个自动锁,每次客人进门时自动把门锁上,等锤子造好了再开锁。这样限制两个客人同时对懒汉铁匠进行访问。
#include <QCoreApplication>
#include <QDebug>
#include<thread>
#include<mutex>
#include <Windows.h>
using namespace std;
//懒汉单例的线程安全做法
class Hammer
{private:static Hammer* m_pHammer;static mutex m_Mutex;//加一把锁Hammer(){qDebug() << "make a new hammer!";}~Hammer() {}Hammer(const Hammer&) = delete;//单例类建议屏蔽拷贝构造函数Hammer &operator =(const Hammer&rhs) = delete;//单例类建议屏蔽拷贝运算符
public:static Hammer* GetHammer(){if(m_pHammer == nullptr)//双重判断,如果没锤子,估计会发生客人同时来店的情况,先锁上,然后再看有没有锤子{m_Mutex.lock();if(m_pHammer == nullptr){//由于静态变量的资源在程序最后全部统一释放,所以这个new没有对应的delete问题不大,严谨的话也可以加上释放过程m_pHammer = new Hammer();}m_Mutex.unlock();}return m_pHammer;}void Beat(){qDebug() << "I can beat";}
};Hammer*Hammer::m_pHammer = nullptr;
mutex Hammer::m_Mutex;void Guest1()//客人1需要锤子
{Sleep(1);Hammer::GetHammer()->Beat();
}void Guest2()//客人2需要锤子
{Sleep(1);Hammer::GetHammer()->Beat();
}int main(int, char **)
{thread thread1(Guest1), thread2(Guest2);thread1.detach();thread2.detach();getchar();return 0;
}
4更优雅的方案
以上几种方式都有一个初始化顺序问题,如果有两个单例,其中一个包含另一个,当被包含的未被初始化时会出问题。《EffectiveC++》item4提到了这一点,并提出一种优雅的解决方案。原文是:“将每个non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference指向它所含的对象。然后用户调用这些函数,而不是直接指涉这些对象。”做法如下:
#include <QCoreApplication>
#include <QDebug>
#include<thread>
#include<mutex>
#include <Windows.h>
using namespace std;
class Hammer
{public:static Hammer& GetHammer();void Beat(){qDebug() << "I can beat";}
private:Hammer(){qDebug() << "make a new hammer!";}~Hammer(){}Hammer(const Hammer&) = delete; //单例类建议屏蔽拷贝构造函数Hammer& operator=(const Hammer&) = delete; //单例类建议屏蔽拷贝运算符
};Hammer& Hammer::GetHammer()
{static Hammer hammer;return hammer;
}void Guest1()//客人1需要锤子
{Sleep(1);Hammer::GetHammer().Beat();
}void Guest2()//客人2需要锤子
{Sleep(1);Hammer::GetHammer().Beat();
}
int main(int, char **)
{thread thread1(Guest1), thread2(Guest2);thread1.detach();thread2.detach();getchar();return 0;
}
可以看出,该方法既简单又线程安全,推荐单例模式采用这种方法。
单例模式(懒汉单例和饿汉单例)相关推荐
- 饿汉单例、懒汉单例类
一.单例模式 可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象. 例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间. 二.饿汉单例类 1.饿汉单例 ...
- java饿汉式有啥作用,Java面试 - 什么是单例设计模式,为什么要使用单例设计模式,如何实现单例设计模式(饿汉式和懒汉式)?...
什么是单例设计模式? 单例设计模式就是一种控制实例化对象个数的设计模式. 为什么要使用单例设计模式? 使用单例设计模式可以节省内存空间,提高性能.因为很多情况下,有些类是不需要重复产生对象的.如果重复 ...
- 003_支持并发的饿汉单例
package com.zr.single;/*** 支持并发的饿汉单例*/ public class HungrySingletion {// 静态初始化器, 由JVM来保证线程安全.private ...
- 002_支持并发的内部类饿汉单例
package com.zr.single;/*** 支持并发的内部类饿汉单例*/ public class HungryInnerSingletion {// 类级的静态的成员式内部类, 该内部类的 ...
- 单例模式可以分为懒汉式和饿汉式: 懒汉式单例模式:在类加载时不初始化。 饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。
单例模式可以分为懒汉式和饿汉式: 懒汉式单例模式:在类加载时不初始化. 饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快.
- 单例模式(懒汉式单例和饿汉式单例)
/** 饿汉式单例*/ class Singleon{private static final Singleon singleon = new Singleon();private Singleon( ...
- 单例设计模式-饿汉式
可以说是单例模式中写法最简单的一个方式,这个就是饿汉式,也就是在类加载的时候,就完成实例化,那他虽然简单,有没有什么值得研究的地方呢,当然是有的,首先我们先完成一个饿汉式的简单实现 package c ...
- 设计模式之单例设计模式(饿汉式)
//饿汉式:类一加载就创建 //创建单例对象 package july.star.thread22;/*** Student* 创建单例对象* @author MoXingJian* @email 9 ...
- 懒汉式单例和饿汉式单例优缺点
1.时间和空间 比较上面两种写法:懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间.当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间. 饿汉式 ...
最新文章
- spring 下载地址
- Windows API一日一练(1)第一个应用程序
- 如何理解Return的返回值?
- Celery的简单使用
- fastreport 旋转90度_水冷必不可少之90度弯头
- gsonformat插件_收藏非常有用的IDEA插件,没用过这些IDEA插件?怪不得写代码头疼
- sql 2008找不到服务器,sql server 2005 数据库迁移问题总结——错误 ‘80004005’ 在 sys.servers 中找不到服务器 ‘XXX’...
- Pytorch快速入门笔记
- JAVA之旅(五)——this,static,关键字,main函数,封装工具类,生成javadoc说明书,静态代码块...
- git .gitignore file does not work
- 【转】无需刻录DMG光盘,教你在VMWare下安装MAC OS X Snow Leopard 10.6
- 遇到问题的时候,要学会问问题
- HTTP Keep-Alive详解[转]
- ios6.x 插件介绍及常用源
- Python笔记 之 矩阵元素选取
- 第一章 Eviews10下载及安装和数据录入
- BM3D 算法原理详细解析 按过程步骤讲解(附C++实现代码)
- Cisco交换机 ——链路聚合
- 向上滚动的文字的淡入淡出效果
- EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE