C++的双重检查锁并不安全(转)

一个典型的单例模式构建对象的双重检查锁如下:

 1 static Singleton * getSingleObject()
 2     {
 3         if(singleObject==NULL)
 4         {
 5             lock();
 6             if(singleObject==NULL)
 7             {
 8                 singleObject = new Singleton();
 9             }
10             unlock();
11         }
12         return singleObject;
13     }

该代码的逻辑是:getSingleObject()函数获得对象,如果对象不存在则创建,反之则直接返回。考虑到线程安全,创建对象时需要加锁(否则可能多线程同时调用该函数时创建多个对象)。同时为了避免线程每次调用getSingleObject()函数时候都要被锁住等待,减小线程间的开销,使用双重检查锁,即只有对象不存在时才锁住等待。

看起来天衣无缝……

C++在new一个对象时,首先会把指针指向分配的那块空间,然后在初始化该空间。

这样假设线程A执行到new该对象时,分配了指针,但是还未来得及构建那块空间。此时线程B执行getSingleObject()函数,由于指针已经分配所以直接返回该地址(但其实是一块还未初始化的地址),线程B操作该地址,game over。。

一种解决方法,但还是有可能出错:

 1 class Singleton {
 2 public:
 3 static volatile Singleton* volatile instance();
 4 ...
 5 private:
 6 // one more volatile added 加入另一个volatile声明
 7 static volatile Singleton* volatile pInstance;
 8 };
 9 // from the implementation file 实现文件内容如下
10 volatile Singleton* volatile Singleton::pInstance = 0;
11 volatile Singleton* volatile Singleton::instance() {
12 if (pInstance == 0) {
13 Lock lock;
14 if (pInstance == 0) {
15 // one more volatile added 加入另一个volatile声明
16 volatile Singleton* volatile temp =
17 new volatile Singleton;
18 pInstance = temp;
19 }
20 }
21 return pInstance;
22 }
时间: 2024-08-18 16:15:01

C++的双重检查锁并不安全(转)的相关文章

[杂谈]C++的双重检查锁并不安全

原文地址 http://www.cnblogs.com/hebaichuanyeah/p/6298513.html 一个典型的单例模式构建对象的双重检查锁如下: static Singleton * getSingleObject() { if(singleObject==NULL) { lock(); if(singleObject==NULL) { singleObject = new Singleton(); } unlock(); } return singleObject; } 该代码

线程安全的单例模式及双重检查锁—个人理解

在web应用中服务器面临的是大量的访问请求,免不了多线程程序,但是有时候,我们希望在多线程应用中的某一个类只能新建一个对象的时候,就会遇到问题. 首先考虑单线程,如果要求只能新建一个对象,那么构造函数我们要设为private.简单的想法: class singleton{ private singleton(){ //..... } private static singleton instance; public static singleton getinstance(){ if(insta

单例模式中用volatile和synchronized来满足双重检查锁机制

背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1:没有volatile修饰的uniqueInstance public class Singleton { private static Singleton uniqueInstance; private Singleton(){ } public static Singleton getInsta

关于并发场景下,通过双重检查锁实现延迟初始化的优化问题隐患的记录

首先,这个问题是从<阿里巴巴Java开发手册>的1.6.12(P31)上面看到的,里面有这样一句话,并列出一种反例代码(以下为仿写,并非与书上一致): 在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化问题隐患,推荐解决方案中较为简单的一种(适用于JDK5及以上的版本),即目标属性声明为volatile型. 1 public class Singleton { 2 private static Singleton instance=null; 3

从单例的双重检查锁想到的

常说的单例有懒汉跟饿汉两种写法.饿汉由于类加载的时候就创建了对象,因此不存在并发拿到不同对象的问题,但会由于开始就加载了对象,可能会造成一些启动缓慢等性能问题:而懒汉虽然避免了这个问题,但普通的写法会在高并发环境下创建多个对象,单纯加synchronize又会明显降低并发效率,较好的两种写法是静态内部类跟双重检查锁两种. 双重检查锁这个,大家都很熟悉了,上代码: public class SingleTest { private static SingleTest singleTest; //获

单例模式中 的 双重检查锁 概念与用法

public class Singleton { //私有的 静态的 本类属性 private volatile static Singleton _instance; //私有化构造器 private Singleton() {} /* * 1st version: creates multiple instance if two thread access * this method simultaneouslyX */ public static Singleton getInstance

单例陷阱——双重检查锁中的指令重排问题

之前我曾经写过一篇文章<单例模式有8种写法,你知道么?>,其中提到了一种实现单例的方法-双重检查锁,最近在读并发方面的书籍,发现双重检查锁使用不当也并非绝对安全,在这里分享一下. 单例回顾 首先我们回顾一下最简单的单例模式是怎样的? /** *单例模式一:懒汉式(线程安全) */ public class Singleton1 { private static Singleton1 singleton1; private Singleton1() { } public static Singl

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

单例创建模式是一个通用的编程习语.和多线程一起使用时,必需使用某种类型的同步.在努力创建更有效的代码时,Java 程序员们创建了双重检查锁定习语,将其和单例创建模式一起使用,从而限制同步代码量.然而,由于一些不太常见的 Java 内存模型细节的原因,并不能保证这个双重检查锁定习语有效. 它偶尔会失败,而不是总失败.此外,它失败的原因并不明显,还包含 Java 内存模型的一些隐秘细节.这些事实将导致代码失败,原因是双重检查锁定难于跟踪.在本文余下的部分里,我们将详细介绍双重检查锁定习语,从而理解它

为什么双重检查锁模式需要 volatile ?

双重检查锁定(Double check locked)模式经常会出现在一些框架源码中,目的是为了延迟初始化变量.这个模式还可以用来创建单例.下面来看一个 Spring 中双重检查锁定的例子. 这个例子中需要将配置文件加载到 handlerMappings中,由于读取资源比较耗时,所以将动作放到真正需要 handlerMappings 的时候.我们可以看到 handlerMappings 前面使用了volatile .有没有想过为什么一定需要 volatile?虽然之前了解了双重检查锁定模式的原理