转载于: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. 饿汉单例、懒汉单例类

    一.单例模式 可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象. 例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间. 二.饿汉单例类 1.饿汉单例 ...

  2. java饿汉式有啥作用,Java面试 - 什么是单例设计模式,为什么要使用单例设计模式,如何实现单例设计模式(饿汉式和懒汉式)?...

    什么是单例设计模式? 单例设计模式就是一种控制实例化对象个数的设计模式. 为什么要使用单例设计模式? 使用单例设计模式可以节省内存空间,提高性能.因为很多情况下,有些类是不需要重复产生对象的.如果重复 ...

  3. 003_支持并发的饿汉单例

    package com.zr.single;/*** 支持并发的饿汉单例*/ public class HungrySingletion {// 静态初始化器, 由JVM来保证线程安全.private ...

  4. 002_支持并发的内部类饿汉单例

    package com.zr.single;/*** 支持并发的内部类饿汉单例*/ public class HungryInnerSingletion {// 类级的静态的成员式内部类, 该内部类的 ...

  5. 单例模式可以分为懒汉式和饿汉式:     懒汉式单例模式:在类加载时不初始化。     饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。

     单例模式可以分为懒汉式和饿汉式: 懒汉式单例模式:在类加载时不初始化. 饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快.

  6. 单例模式(懒汉式单例和饿汉式单例)

    /** 饿汉式单例*/ class Singleon{private static final Singleon singleon = new Singleon();private Singleon( ...

  7. 单例设计模式-饿汉式

    可以说是单例模式中写法最简单的一个方式,这个就是饿汉式,也就是在类加载的时候,就完成实例化,那他虽然简单,有没有什么值得研究的地方呢,当然是有的,首先我们先完成一个饿汉式的简单实现 package c ...

  8. 设计模式之单例设计模式(饿汉式)

    //饿汉式:类一加载就创建 //创建单例对象 package july.star.thread22;/*** Student* 创建单例对象* @author MoXingJian* @email 9 ...

  9. 懒汉式单例和饿汉式单例优缺点

    1.时间和空间 比较上面两种写法:懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间.当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间. 饿汉式 ...

最新文章

  1. spring 下载地址
  2. Windows API一日一练(1)第一个应用程序
  3. 如何理解Return的返回值?
  4. Celery的简单使用
  5. fastreport 旋转90度_水冷必不可少之90度弯头
  6. gsonformat插件_收藏非常有用的IDEA插件,没用过这些IDEA插件?怪不得写代码头疼
  7. sql 2008找不到服务器,sql server 2005 数据库迁移问题总结——错误 ‘80004005’ 在 sys.servers 中找不到服务器 ‘XXX’...
  8. Pytorch快速入门笔记
  9. JAVA之旅(五)——this,static,关键字,main函数,封装工具类,生成javadoc说明书,静态代码块...
  10. git .gitignore file does not work
  11. 【转】无需刻录DMG光盘,教你在VMWare下安装MAC OS X Snow Leopard 10.6
  12. 遇到问题的时候,要学会问问题
  13. HTTP Keep-Alive详解[转]
  14. ios6.x 插件介绍及常用源
  15. Python笔记 之 矩阵元素选取
  16. 第一章 Eviews10下载及安装和数据录入
  17. BM3D 算法原理详细解析 按过程步骤讲解(附C++实现代码)
  18. Cisco交换机 ——链路聚合
  19. 向上滚动的文字的淡入淡出效果
  20. EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE

热门文章

  1. JavaScript div跟随鼠标移动
  2. Change 改变事件
  3. 一文教你快速学习搭建属于自己的数据指标体系
  4. .net core 实现微信登陆
  5. LANL Earthquake Prediction收获
  6. 新三板上市和主板上市的区别主要是什么?
  7. 【毕业设计】LSTM预测算法(股票预测 天气预测 房价预测)
  8. 手机电容触摸屏技术简介
  9. 看服务器时间修改日志,怎样看服务器远程更改时间记录
  10. 行之有效的Golang编码规范