单例模式,分为饿汉式单例 和 懒汉式单例。

先把本类对象所需内存在main函数执行前就new出来,这是饿汉式单例。

个人思考:

为什么饿汉式不独霸天下,还有什么必要去研究使用cpp11上支持的双检查锁机制(这是懒汉式,用到类实例时才去申请内存)?就因为饿汉式单例事先就占用了一些类内存?反正迟早都要占用内存啊。

或者说,饿汉式单例有什么缺陷。

饿汉式单例:

个人理解:

优点: 编程上使用很简单

缺点: 整个程序运行期间会一直占用内存,不可以在程序运行期间将其delete。

饿汉式单例模式的重要特点是运用了全局对象的构造过程先于main函数执行之前的特点。

如果程序运行期将实例化的单例对象delete之后,如果有再次创建该单例对象的需求,

正因为饿汉式单例的上述特点,将无法达到满意的目标效果(目标效果是支持线程安全的单例模式)。

因为程序不可能重新从main函数前再重头执行一次(在嵌入式平台,只有设备重新上电了)。

即: 饿汉式单例模式不支持动态创建、销毁单例对象。

普通的懒汉式单例 动态支持的单例对象的申请和释放

class Singleton{
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static Singleton* m_instance;
};
//线程安全,但锁的代价过高
Singleton* Singleton::getInstance() {
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}

普通的懒汉式(该获取实例函数内执行,先上锁,再判断指针,最后分配内存)也是线程安全的,只是其内部实现,即获取实例函数内,不管三七二十一,每次都先上锁,考虑到锁的代码过高,不满足高并发编程要求

普通的懒汉式单例也适用于我们针对大多数场景使用,因为,大多时候,嵌入式程序员不需要考虑高并发场景。  

普通的双检查锁

//普通写法的双检查锁,但由于内存读写reorder, 所以是线程不安全
Singleton* Singleton::getInstance() { if(m_instance==nullptr){
Lock lock;
if (m_instance == nullptr) { // 这句代码并不是多余的,有其作用
m_instance = new Singleton();
}
}
return m_instance;
}

 reorder详解:

假设某个时刻:

线程A 执行到m_instance = new Singleton(); 并且已经完成步骤1和 步骤3, 但是步骤2尚未执行,也就是说,虽然此时m_instance已经不是NULL,但是其

所指向的内存尚未完成构造。

此时,线程B被调度,执行getInstance(),进入上述函数内部,先判断if(m_instance==nullptr)(注意,这句代码是未上锁的,所以B线程可以执行),由于此时m_instance已经不是NULL,所以该函数即将退出,

线程B认为自己已经获取到了该单实例的句柄,接下来就很有可能使用该单实例的句柄进行操作。 显然,这不是线程安全的。

另外,解释下第二个if判断为什么不是多余的:

线程A有可能在执行第一个if判断后,立即被调度到线程B执行,而此时线程A尚未执行到Lock lock;的这句上锁代码。m_instance被线程B实例化(完成了单例模式整个过程),

再次调度回线程A时,线程A继续执行上锁代码,此时,有必要再次判断m_instance指针是否为空,如果已经是非空,则不能执行单例类的构造和赋值。

上述的双检查锁的代码,整体代码逻辑是没问题的,虽然是线程非安全的,但这不是程序员能够解决的了。究其原因,此处线程非安全是因为reorder机制。

所以,我们程序员需要借助编译器的新特性才能解决该问题。

线程安全的双检查锁 :  从支持C++ 11特性的编译器开始,提供了通用的跨平台实现。

//线程安全的双检查锁 -- C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex; Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}

使用cpp11特性支持的双检查方式的懒汉式单例不是必须的,只是这种方式是专用于高并发场景下的,满足高并发要求(ps:这种方式一定是线程安全的)。

补充点:

全局变量的构造时机,和main函数被执行的时机。

全局变量的构造,这是crt (c run time)做的事情,它保证全局变量初始化在main之前运行。

编写本博客参考过的博客:

https://www.cnblogs.com/goodAndyxublog/p/11356402.html

.

单例模式,reorder详解,线程安全,双检查锁的更多相关文章

  1. Java基础学习(五)-- Java中常用的工具类、枚举、Java中的单例模式之详解

    Java中的常用类 1.Math : 位于java.lang包中 (1)Math.PI:返回一个最接近圆周率的 (2)Math.abs(-10):返回一个数的绝对值 (3)Math.cbrt(27): ...

  2. DCL双检查锁机制实现的线程安全的单例模式

    public class MyObject { private volatile static MyObject myObject; private MyObject(){} public stati ...

  3. java多线程环境单例模式实现详解

    Abstract 在开发中,如果某个实例的创建需要消耗很多系统资源,那么我们通常会使用惰性加载机制,也就是说只有当使用到这个实例的时候才会创建这个实例,这个好处在单例模式中得到了广泛应用.这个机制在s ...

  4. Java单例模式深入详解

    原文地址:http://www.cnblogs.com/hxsyl/ 仅作为笔记收藏…… 一.问题引入 偶然想想到的如果把Java的构造方法弄成private,那里面的成员属性是不是只有通过stati ...

  5. 详解线程池execute和submit用法

    在使用线程池时,我们都知道线程池有两种提交任务的方式,那么他们有什么区别呢? 1.execute提交的是Runnable类型的任务,而submit提交的是Callable或者Runnable类型的任务 ...

  6. Java Singleton(单例模式) 实现详解

    什么是单例模式? Intend:Ensure a class only has one instance, and provide a global point of access to it. 目标 ...

  7. IOS开发中单例模式使用详解

    第一.基本概念 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问. 第二.在IOS中使用单例模式的情 ...

  8. android开发学习 ------- 【转】 android中的单例模式 (详解)

    https://blog.csdn.net/u011418943/article/details/60139644     这篇文章 前因后果 都说出来了 ,值得学习. https://blog.cs ...

  9. 详解线程池的作用及Java中如何使用线程池

    服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发.耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作.常规的方法是针对一个新的请求创建一个新线 ...

随机推荐

  1. soso官方:基于相关排序的判断

    http://www.wocaoseo.com/thread-186-1-1.html 议程 概述 检索词 用户的信息需求 网页的自有信息 网页的附属信息 相关性的计算框架 概述 相关性的表象 检索词 ...

  2. 力扣Leetcode 3. 无重复字符的最长子串

    无重复字符的最长子串 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串 ...

  3. Python爬取网易云音乐歌手歌曲和歌单

    仅供学习参考 Python爬取网易云音乐网易云音乐歌手歌曲和歌单,并下载到本地 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做 ...

  4. new Map()详细介绍与对比

      说明: Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现.如果你需要“键值对”的数据结构,Map比Object更合适.它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串, ...

  5. P4719 【模板】"动态 DP"&动态树分治

    题目描述 给定一棵 n 个点的树,点带点权. 有 m 次操作,每次操作给定 x,y,表示修改点 x 的权值为 y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入格式 第一行有两个整数 ...

  6. Unity技巧集合

    地址:http://blog.csdn.net/stalendp/article/details/17114135 这篇文章将收集unity的相关技巧,会不断地更新内容. 1)保存运行中的状态 uni ...

  7. lidar激光雷达领域的分类

    lidar领域可以按分为以下五方面: 激光雷达系统与装备 激光雷达系统与开发 激光雷达光源 激光雷达探测 多光谱激光雷达系统 单光子激光雷达系统 低成本RGB-D距离传感器 激光雷达元器件及装备等 激 ...

  8. 小白也能弄懂的卷积神经网络(Convolutional Neural Networks )

    本系列主要是讲解卷积神经网络 - Convolutional Neural Networks 的系列知识,本系列主要帮助大家入门,我相信这是所有入门深度学习的初学者都必须学习的知识,这里会用更加直接和 ...

  9. Zabbix icmp pinger processes more than 75% busy

    Zabbix icmp pinger processes more than 75% busy   Zabbix server报"Zabbix icmp pinger processes m ...

  10. Zabbix 5.0 LTS版本的安装小结

    Zabbix 5.0 LTS版本的安装小结   1:准备Zabbix的服务器. 这里可能需要一台或多台服务器,视需求和资源而定.也可以将Zabbix_Server.MySQL.Zabbix Web等安 ...