DCL的单例一定是线程安全的吗

读了本文,你会知道,为什么不加volatile关键字的单例模式不是线程安全的

有经验的开发者都知道双重锁定检查(DCL,Double Check Lock)的单例是最优秀的,如下文所示:

 1 public class Singleton {
 2     private static Singleton instance = null;
 3     public  static Singleton getInstance() {
 4         if(null == instance) {    // 第一次检查
 5             synchronized (Singleton.class) {
 6                 if(null == instance) {   // 加锁后第二次检查
 7                     instance = new Singleton();
 8                 }
 9             }
10         }
11
12         return instance;
13
14     }
15 }

这看上去一切都很完美,无懈可击,但实际上这个 getInstance() 方法并不完美。问题出在哪里呢?出在 new 操作上,我们以为的 new 操作应该是:

  1. 分配一块内存 M;
  2. 在内存 M 上初始化 Singleton 对象;
  3. 然后 M 的地址赋值给 instance 变量。

但是实际上,经过编译器优化后的执行顺序是这样的:

  1. 分配一块内存 M;
  2. 将 M 的地址赋值给 instance 变量;
  3. 最后在内存 M 上初始化 Singleton 对象。

如下图所示,线程A进入<第一次检查>,A先获得Synchronize锁,分配一块内存M,先将M的地址赋值给了 instance 变量, 此刻发生线程切换,线程B检测到instance不为空,直接返回未初始化的instance, 如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。

所以,我们会加对象上加 Volatile关键字,禁止指令重排序,

Volatile的原理,可以参考我的另一篇文章:Java多线程的volatile底层实现原理

原文地址:https://www.cnblogs.com/amberJava/p/12546798.html

时间: 2024-11-19 19:29:53

DCL的单例一定是线程安全的吗的相关文章

【Spring】8、Spring框架中的单例Beans是线程安全的么

看到这样一个问题:spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的.如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全. 最浅显的解决办法就是将多态bean的作用域由"singleton&

【黑马】程序员————多线程(二)单例设计模式、线程间通信,JDK1.5互斥锁

------Java培训.Android培训.iOS培训..Net培训.期待与您交流!----- 一.单例设计模式 单例设计模式的意义: A.保证类在内存中只有一个对象,不提供外部访问方式,构造函数用private修饰. B.提供公共方法(static修饰,类的静态方法),获取类的实例.单例设计模式分为饿汉和懒汉两种模式. 饿汉式&懒汉式 class Test33 { public static void main(String[] args) { Fanjianan.getInstance()

单例设计模式-(你确定自己写的懒汉单例真的是线程安全的吗)

1.单例设计模式的优缺点 优点: 1):只创建一个实例,就可以到处使用,加快创建实体的效率 缺点: 1):如果使用的频率比较低,实例会一直占据着内存空间,会造成资源浪费 2):可能会出现线程安全问题 2.单例设计模式的两种定法(饿汉.懒汉) 饿汉方法写法:(可能会造成资源浪费,类一被加载就创建了实例,但并不能确保这个实例什么时候会被用上) package com.zluo.pattern; /** * * 项目名称:single-instance <br> * 类名称:SingleInstan

透过DCL单例实现谈谈安全发布

1.不安全的发布 线程之间共享数据时,就是在"发布一个共享对象"与"另一个线程访问该对象"之间缺少一种Happens-Before关系(比如A.B两个线程,想要保证操作B的线程看到操作A的结果,那么A与B必须满足Happens-Before关系)时,就可能出现重排序问题.在没有充分同步的情况下,发布一个对象可能导致另一个线程看到一个只被部分构造的对象. 先来看个双重检查加锁(DCL)单例实现 /** * DCL单例 * @author renhj * */ publ

深入理解单例模式:静态内部类单例原理

本文主要介绍java的单例模式,以及详细剖析静态内部类之所以能够实现单例的原理.OK,废话不多说,进入正文. 首先我们要先了解下单例的四大原则: 1.构造私有. 2.以静态方法或者枚举返回实例. 3.确保实例只有一个,尤其是多线程环境. 4.确保反序列换时不会重新构建对象. 我们常用的单例模式有: 饿汉模式.懒汉模式.双重锁懒汉模式.静态内部类模式.枚举模式,我们来逐一分析下这些模式的区别. 1.饿汉模式: public class SingleTon{ private static Singl

Java-----关于单例设计模式

1. 单例模式DCL写法 单例设计模式中,有一种双重检查锁的写法, 也就是所谓的懒汉式 class Single{ private static Single sSingle; private Single() {} public static Single getInstance() { if(sSingle == null) { synchronized(Single.class) { if(sSingle == null) { sSingle = new Single(); } } } r

Swift中编写单例的正确方式

Swift中编写单例的正确方式 2015-12-07 10:23 编辑: yunpeng.hu 分类:Swift 来源:CocoaChina翻译活动 14 10647 Objective-CSwift单例 招聘信息: Cocos2d-x 工程师 cocos2dx手游客户端主程 wp开发 iOS开发工程师 iOS软件工程师 iOS研发工程师 iOS讲师 iOS开发工程师 iOS高级开发工程师 iOS 高级软件工程师 iOS高级开发工程师 本文由CocoaChina译者leon(社区ID)翻译自kr

servlet 单例问题

servlet是单例的,即在web服务器中最多只存在servlet的一个实例,那么servlet就是线程不安全的.下面通过以下代码模拟买票过程: package com.zjb.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServ

静态类和单例区别

近期在看android的网络访问类的时候发现有些人用静态类来封装,有些人用单例来封装,突然发现,两种都可以,那究竟使用哪种好呢?这个答案并非是绝对的,两种都可以.就像是抽象类和接口一样,其实很多情况两种方式都可以达到目的,就是看个人理解(抽象类和接口的区别看我的另外一篇文章 接口和抽象类区别).以下是个人的一些看法. 静态类就像是面向过程时代的方法与一样,是一个工具类,很少依赖其他对象或者资源,只是在面向对象的思想中,一定要用类,而不用像面向过程时代直接调用方法,所以用面向对象封装的特性,才用静