双重检查锁定(Double Check Lock,DCL)

1、懒汉式单例模式,无法保证线程安全:

  1. public class Singleton {
  2. private static Singleton singleton;
  3.  
  4. private Singleton() {
  5. }
  6.  
  7. public static Singleton getInstance() {
  8. if (singleton == null) {// 多个线程同时执行到此,会生成多个Singleton实例
  9. singleton = new Singleton();
  10. }
  11.  
  12. return singleton;
  13. }
  14. }

2、同步处理,synchronized就会导致这个方法比较低效:

  1. public class Singleton {
  2. private static Singleton singleton;
  3.  
  4. private Singleton() {}
  5.  
  6. public static synchronized Singleton getInstance() {
  7. if (singleton == null) {
  8. singleton = new Singleton();
  9. }
  10.  
  11. return singleton;
  12. }
  13. }

3、双重检查 DCL:

  1. public class Singleton {
  2. private static Singleton singleton;
  3. Integer a;
  4.  
  5. private Singleton(){}
  6.  
  7. public static Singleton getInstance(){
  8. if(singleton == null){ // 1 只有singleton==null时才加锁,性能好
  9. synchronized (Singleton.class){ //
  10. if(singleton == null){ //
  11. singleton = new Singleton(); //
  12. }
  13. }
  14. }
  15. return singleton;
  16. }
  17. }

但是,仍然有问题!!

创建对象过程:

(1)分配内存空间

(2)初始化对象

(3)将内存空间的地址赋值给对应的引用

(2)(3)会被处理器优化,发生重排序

举例:

A线程singleton = new Singleton()发生重排序,将分配的内存空间引用赋值给了静态属性singleton(即singleton != null),而对象还未初始化(即Integer a == null);

B线程此时调用getInstance()方法,因为singleton != null,直接返回singleton。当B线程使用singleton的a属性时就会空指针。

分析:

问题在于singleton = new Singleton()的重排序

(1)不允许初始化阶段步骤2 、3发生重排序。

(2)允许初始化阶段步骤2 、3发生重排序,但是不允许其他线程“看到”这个重排序。

解决:

1、利用volatile限制重排序

  1. public class Singleton {
  2. private volatile static Singleton singleton;// 通过volatile关键字来确保安全
  3.  
  4. private Singleton(){}
  5.  
  6. public static Singleton getInstance(){
  7. if(singleton == null){
  8. synchronized (Singleton.class){
  9. if(singleton == null){
  10. singleton = new Singleton();
  11. }
  12. }
  13. }
  14. return singleton;
  15. }
  16. }

(1)分配内存空间

(2)初始化对象

(3)将内存空间的地址赋值给对应的引用

第(3)步 volatile修饰的变量singleton的写入操作,通过内存屏障限制的重排序 参考:Java并发(六):volatile的实现原理

2、利用类初始化

JVM会保证一个类的类构造器在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器,其他线程都需要阻塞等待,直到活动线程执行方法完毕。

特别需要注意的是,在这种情形下,其他线程虽然会被阻塞,但如果执行初始化的那条线程退出后,其他线程在唤醒之后不会再次进入/执行初始化,因为在同一个类加载器下,一个类型只会被初始化一次。

  1. public class Singleton {
  2. private static class SingletonHolder{
  3. public static Singleton singleton = new Singleton();
  4. }
  5.  
  6. public static Singleton getInstance(){
  7. return SingletonHolder.singleton;
  8. }
  9. }

参考资料:

【死磕Java并发】—–Java内存模型之从JMM角度分析DCL

Java并发(七):双重检验锁定DCL的更多相关文章

  1. Java盲点:双重检查锁定及单例模式

    尊重原创: http://gstarwd.iteye.com/blog/692937 2004 年 5 月 01 日 所有的编程语言都有一些共用的习语.了解和使用一些习语很有用,程序员们花费宝贵的时间 ...

  2. JAVA并发七(多线程环境中安全使用集合API)

    在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对 ...

  3. java 并发(七)--- ThreadLocal

         文章部分图片来自参考资料 问题 : ThreadLocal 底层原理 ThreadLocal 需要注意什么问题,造成问题的原因是什么,防护措施是什么 ThreadLocal 概述 Threa ...

  4. Java并发--三大性质

    一.多线程的三大性质 原子性:可见性.有序性 二.原子性 原子性介绍 原子性是指:一个操作时不可能中断的,要么全部执行成功要么全部执行失败,有着同生共死的感觉.即使在多线程一起执行的时候,一个操作一旦 ...

  5. java并发编程系列七:volatile和sinchronized底层实现原理

    一.线程安全 1.  怎样让多线程下的类安全起来 无状态.加锁.让类不可变.栈封闭.安全的发布对象 2. 死锁 2.1 死锁概念及解决死锁的原则 一定发生在多个线程争夺多个资源里的情况下,发生的原因是 ...

  6. DCL,即Double Check Lock,中卫双重检查锁定。

    DCL,即Double Check Lock,中卫双重检查锁定. [Java并发编程]之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码) 关于单例.关于DCL: ...

  7. java中的双重锁定检查(Double Check Lock)

    原文:http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization#theCommentsSect ...

  8. Java并发——DCL问题

    转自:http://www.iteye.com/topic/875420 如果你搜索网上分析dcl为什么在java中失效的原因,都会谈到编译器会做优化云云,我相信大家看到这个一定会觉得很沮丧.很无助, ...

  9. JAVA 双重检查锁定和延迟初始化

    双重检查锁定的由来在Java程序中,有时需要推迟一些高开销的对象的初始化操作,并且只有在真正使用到这个对象的时候,才进行初始化,此时,就需要延迟初始化技术.延迟初始化的正确实现是需要一些技巧的,否则容 ...

随机推荐

  1. Fiddler 抓包工具总结(转)

    阅读目录 1. Fiddler 抓包简介 1). 字段说明 2). Statistics 请求的性能数据分析 3). Inspectors 查看数据内容 4). AutoResponder 允许拦截制 ...

  2. 【Explain】mysql之explain详解(分析索引的最佳使用)

    在日常工作中,我们会有时会开慢查询去记录一些执行时间比较久的SQL语句,找出这些SQL语句并不意味着完事了,些时我们常常用到explain 这个命令来查看一个这些SQL语句的执行计划,查看该SQL语句 ...

  3. nginx配置不当导致的目录遍历下载漏洞-“百度杯”CTF比赛 2017 二月场

    题目:http://98fe42cede6c4f1c9ec3f55c0f542d06b680d580b5bf41d4.game.ichunqiu.com/login.php 题目内容: 网站要上线了, ...

  4. Linux下帮助命令

    帮助命令(各种命令区别)   最常用的帮助命令   help --help help cd 查看内置命令的使用 info man   help cd 查看内置命令的使用   获得帮助的途径:   ma ...

  5. powerpc平台移植zebra或quagga-0.99.23

    1,先configure  ./configure   --enable-vtysh --disable-bgpd --disable-ripd --disable-ripngd --disable- ...

  6. 很多人都没用过的轻量级Oracle数据库数据导出工具SQLLDR2——性能超赞

    SQLLDR2 介绍 每周发表一篇数据库或大数据相关的帖子,敬请关注 1. 工具介绍 Sqluldr2(SQL * UnLoader 第二版)是灵活与强大的 Oracle 文本导出程序,已被大众使 用 ...

  7. mysql 创建,授权,删除 用户

    1.创建用户 创建一个用户名是 lefunyun 密码是 X5A4FU8I0lKM21YPYUzP 账号 CREATE USER lefuyun@localhost IDENTIFIED BY 'X5 ...

  8. 洛谷 P1202 [USACO1.1]黑色星期五Friday the Thirteenth 题解

    题目传送门 这道题暴力就能解决. #include<bits/stdc++.h> using namespace std; int xi; ,ans[]; int main() { int ...

  9. MVC – 8.Razor 布局

    8.1.@RenderBody() 8.2.多个"占位符":@RenderSection() 8.3.js合并 @Scripts.Render("~/bundles/js ...

  10. spring-cloud-sleuth+zipkin追踪服务实现(二)

    1. 简述 在上一节<spring-cloud-sleuth+zipkin追踪服务实现(一)>中,我们使用microservice-zipkin-server.microservice-z ...