问题

在设计模式中,有一个很经典的模式-单例模式,它可能是实现上最简单的模式,在代码中也经常使用,在单线程下,毫无疑问延迟化加载是比较常用的,但是在多线程条件下,单例模式的延迟加载可能就会出现一些问题。

如以下的代码:

T* GetInstance()
{if (pInst == NULL){pInst = new T;}return pInst;
}

如果检测代码和实例化代码不是同一线程,则很容易出现返回NULL的现象。

经典的单例模式下的双重检测

解决以上问题就是加并发锁,我们将需要实例化的对象加锁,于是有了以下代码:

T* GetInstance()
{if (pInst == NULL){lock();if (pInst == NULL)pInst = new T;unlock();}return pInst;
}

为什么要用两层if检查,第一层的if检查是因为当实例为空的时候,才去对实例加锁,这样可以避免多次对lock资源的调用,当第二层if检测的时候,才是程序要对程序进行初始化。

乍看这种代码是没有问题的,但是问题的来源是CPU的乱序执行,C++的New操作实际上包含了两个步骤:

  1. 分配内存
  2. 调用构造函数

所以pInst = new T包含了三个步骤:

  1. 分配内存
  2. 在内存的位置上调用构造函数
  3. 将内存的地址赋值给pInst

因为(2)和(3)是可以颠倒的,所以可以出现这样的情况:pInst的值已经不是NULL,但对象仍然没有构造完毕。如果另外一个线程对GetInstance的调用,此时第一个if为false,这样就会返回一个未构造完成的对象,此时可能会导致程序崩溃。

解决思路

许多体系结构都提供barrier指令,POWERPC提供了其中一条名为lwsync的指令,我们可以这样来保证线程安全:

#define barrier() __asm__ volatile ("lwsyc")
volatile T* pInst = 0;
T* GetInstance()
{if (!pInst) {lock();if (!pInst){T* temp = new T;barrier()pInst = temp;}unlock();}return pInst;
}

由于barrier的存在,对象的构造一定会在barrier执行之前完成,所以这样不会出现一些问题。

[C++][线程安全]单例模式下双检查锁和线程相关推荐

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

    实现线程安全的单例设计模式的三种方式: DCL双检查锁机制实现线程安全 使用静态内置类实现线程安全 使用static代码块实现线程安全 -------------------------------- ...

  2. 2021面试 Lock,synch,dcl双检查锁sy+volite,悲观锁,偏向,轻量锁,重量锁,升级12

    0.数据库悲观锁:for update: MySQL实现悲观锁_九色鹿-CSDN博客_mysql悲观锁怎么实现 1. ReentrantLock锁公平与非公平实现.重入原理:  ReentrantLo ...

  3. java并发编程(二十六)——单例模式的双重检查锁模式为什么必须加 volatile?

    前言 本文我们从一个问题出发来进行探究关于volatile的应用. 问题:单例模式的双重检查锁模式为什么必须加 volatile? 什么是单例模式 单例模式指的是,保证一个类只有一个实例,并且提供一个 ...

  4. 《面向模式的软件体系结构2-用于并发和网络化对象模式》读书笔记(13)--- 线程安全接口和双检查加锁优化...

    4.3线程安全接口(Thread-Safe Interface) 1.问题 多线程组件通常包括多个可被公共访问的接口方法以及可以改变组件状态的私有方法.为了避免出现竞争条件,可以使用一个组件内部的锁对 ...

  5. 单例模式之双重检查锁(double check locking)的发展历程

    不安全的单例 没有注意过多线程安全问题的时候,我们的单例可能是这样的: public final class Singleton {private static Singleton instance; ...

  6. Java单例模式中双重检查锁的问题

    单例创建模式是一个通用的编程习语.和多线程一起使用时,必需使用某种类型的同步.在努力创建更有效的代码时,Java 程序员们创建了双重检查锁定习语,将其和单例创建模式一起使用,从而限制同步代码量.然而, ...

  7. Python 线程(二):简单锁实现线程同步

    Python中有两种锁,一个锁是原始的锁(原语), 不可重入,而另一种锁则是可重入的锁即递归锁.而是thread模块中,只提供了不可重入的锁,而在threading中则提供这两种锁. 可重入:当一个线 ...

  8. Java的懒汉式双检锁单例模式

    Java的懒汉式双检锁单例模式 文章目录 Java的懒汉式双检锁单例模式 一. 实现一个双检锁 二. 为什么线程不安全 三. 关于指令重排序 四. 关于原子操作 五. 实现线程安全的双检锁 首先回忆一 ...

  9. JAVA的多线程、死锁、线程间通信、如何规避死锁、线程安全的单例模式

    主要内容: 多线程 线程和进程间的关系 Java中的线程理论 Java中线程类的实现方式 Java中线程的常用方法 线程安全性问题 线程间通信 线程的死锁 如何规避死锁 线程安全的单例模式 多线程 线 ...

最新文章

  1. Nginx之HTTP过滤模块
  2. AI芯片评测如何与时俱进?地平线提出全新MAPS评测方法,帮助用户理解AI芯片性能...
  3. C++ 内连接与外连接 (转)
  4. nginx报错:nginx: [alert] could not open error log file: open() “/var/log/nginx/error.log“ failed (2: N
  5. WINDOWS下的squid
  6. LeetCode 1026. 节点与其祖先之间的最大差值(二叉树DFS)
  7. 使用maven 创建Quartz 任务示例_01
  8. 【NOIp2002】矩形覆盖
  9. openstack租户管理_几大OpenStack的精华问答 | OpenStack都有哪些基础服务?
  10. 第十章:内核同步方法
  11. Anaconda python安装使用
  12. mysql用户管理--密码管理
  13. 信息安全技术及应用 互联网安全协议
  14. 客户端之H5拉起第三方app并跳转到指定页面
  15. 拉姆达表达式/Lambda表达式/lambda expression 使用整理
  16. 支付宝小程序登录PHP
  17. python基础入门(变量)
  18. Pr入门学习之选择GPU加速
  19. html清理超链接前面的黑点,吹毛求疵:解决IE6-7给链接加黑点边框的三种方案
  20. AIX7.1 安装配置 EMC CLARiiON 存储驱动软件

热门文章

  1. localStorage封装借口store.js的使用
  2. [译] SpaceAce 了解一下,一个新的前端状态管理库
  3. 一套比较完整的前端技术选型,需要规整哪些东西,你知道不?
  4. 生产上完成TopN统计流程
  5. mysql 字符串类型 char varchar
  6. Linux下面的IO模型
  7. Eclipse中classpath和deploy assembly的文件位置
  8. Object之MemberwiseClone方法
  9. 计算机本地网络如何共享,本地网络共享怎么实现
  10. 用java写个简单的直播强求_全网最简单易懂的Netty入门示例,再不会用Netty我直播吃翔...