背景

最近在学习设计模式的时候看到了单例模式,里面还是有很多内容的,比如双重检查锁方式实现的单例模式,就是一个面试考点,接下来我们就来详细说说。

单例模式

单例模式很简单,就是在构造函数中多了加一个构造函数,访问权限是 private 的就可以了,这个模式是简单,但有时候简单的东西也很容易出问题。在高并发的项目中,每个请求都要创建同一个单例对象。如果没有控制好,创建了多个单例对象,那就会导致业务逻辑混乱,数据一致性校验失败等复杂的问题,而且难以排查。为了解决这一问题,我们可以使用双重检查锁实现单例模式。

单例模式-双重检查锁(DCL, 即 double-checked locking)

实现代码如下:

package com.hsy.demo;/*** 懒汉单例** 优点:懒加载,线程安全,效率较⾼* 缺点:实现较复杂** @date 2022-04-01 11:08:26* @since*/
public class LazySingletonDCL {/*** 私有构造函数** @param* @author Huangshaoyang* @date 2022-04-01 11:04:10* @since*/private LazySingletonDCL() {}/*** 定义⼀个静态变量指向⾃⼰类型** volatile 避免DCL 失效*/private volatile static LazySingletonDCL lazySingleton = null;/*** 获取实例方法** @param* @author Huangshaoyang* @date 2022-04-01 10:04:33* @since*/public static LazySingletonDCL getLazySingleton() {// 第⼀重检查是否为 nullif (lazySingleton == null) {// 使⽤ synchronized 加锁synchronized (LazySingletonDCL.class) {// 第二重检查是否为 nullif (lazySingleton == null) {lazySingleton = new LazySingletonDCL();}}}return lazySingleton;}}

这种实现方式的优点:懒加载,线程安全,效率较⾼

这种实现方式的缺点:实现较复杂

实现原理

这⾥的双重检查是指两次⾮空判断,锁指的是 synchronized 加锁,为什么要进⾏双重判断,其实很简单,第⼀重判断,如果实例已经存在,那么就不再需要进⾏同步操作,⽽是直接返回这个实例,如果没有创建,才会进⼊同步块,同步块的⽬的与之前相同,⽬的是为了防⽌有多个线程同时调⽤时,导致⽣成多个实例,有了同步块,每次只能有⼀个线程调⽤访问同步块内容,当第⼀个抢到锁的调⽤获取了实例之后,这个实例就会被创建,之后的所有调⽤都不会进⼊同步块,直接在第⼀重判断就返回单例。

关于内部的第⼆重空判断的作⽤,当多个线程⼀起到达锁位置时,进⾏锁竞争,其中⼀个线程获取锁,如果是第⼀次进⼊则为 null,会进⾏单例对象的创建,完成后释放锁,其他线程获取锁后就会被空判断拦截,直接返回已创建的单例对象。

其中最关键的⼀个点就是 volatile 关键字的使⽤,关于 volatile 的详细介绍可以直接搜索 volatile 关键字即可,有很多写的⾮常好的⽂章,这⾥不做详细介绍。

简单说明⼀下,双重检查锁中使⽤ volatile 的两个重要特性:可⻅性、禁⽌指令重排序

这⾥为什么要使用volatile ?

这是因为 new 关键字创建对象不是原⼦操作,创建⼀个对象会经历下⾯的步骤:

  1. 在堆内存开辟内存空间

  2. 调⽤构造⽅法,初始化对象

  3. 引⽤变量指向堆内存空间

对应字节码指令如下:

9: astore_0
10: monitorenter  #synchronized锁
11: getstatic
14: ifnonnull
17:new
20:dup
21:invokespecial
24:putstatic
27: aload_0
28: monitorexit
29: goto

为了提⾼性能,编译器和处理器常常会对既定的代码执⾏顺序进⾏指令重排序,从源码到最终执⾏指令会经历如下流程:

1、源码

2、编译器优化重排序

3、指令级并⾏重排序

4、内存系统重排序

5、最终执⾏指令序列

所以经过指令重排序之后,创建对象的执⾏顺序可能为17、21、24或者17、24、21,因此当某个线程在乱序运⾏17、24、21指令的时候,引⽤变量指向堆内存空间,这个对象不为 null,但是没有初始化,其他线程有可能这个时候进⼊了 getInstance 的第⼀个 if(instance == null) 判断不为 nulll ,导致错误使用了没有初始化的非空实例,这样的话就会出现异常,这个就是著名的DCL 失效问题。

当我们在引⽤变量上⾯添加 volatile 关键字以后,会通过在创建对象指令的前后添加内存屏障来禁⽌指令重排序,就可以避免这个问题,⽽且对volatile 修饰的变量的修改对其他任何线程都是可⻅的。

THE END.

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=staxjsob8l25

面试问到DCL失效不知所措相关推荐

  1. java dcl 失效解决_DCL失效原因和解决方案

    Java内存模型    在了解Java的同步秘密之前,先来看看JMM(Java Memory Model). Java被设计为跨平台的语言,在内存管理上,显然也要有一个统一的模型.而且Java语言最大 ...

  2. 懵了,Java枚举单例模式比DCL和静态单例要好???

    点击关注公众号,实用技术文章及时了解 来源:liuchenyang0515.blog.csdn.net/article/ details/121049426 文章目录 双重校验锁单例(DCL) 为什么 ...

  3. 设计模式之单例模式学习笔记

    前言 单例模式是最常用到的设计模式之一,比如封装网络请求框架时,RxJava+Retrofit的封装过程就用到了单例模式.顾名思义,保证一个类只有一个实例,并提供一个访问它的全局访问点.也就是说,要确 ...

  4. 设计模式之一:单例模式

    设计模式之一:单例模式 目录介绍 1.单例模式介绍 2.单例模式定义 3.单例模式使用场景 4.单例模式的实现方式 4.1 懒汉式[线程不安全] 4.2 懒汉式[**synchronized 线程安全 ...

  5. 2017 年初、阿里、腾讯、百度、华为、京东、搜狗和滴滴面试题汇集(更新篇)...

    欢迎Follow我的GitHub, 关注我的CSDN. 其余参考Android目录.已同步微信公众号:猛戳这里 本文在我的微信公众号:原创 杨守乐 首发. 转载请标明出处谢谢: http://blog ...

  6. Java并发编程(三)volatile域

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Android多线程(一)线程池 Android多线程(二)AsyncTask源代码分析 前言 有时仅仅为了读写一个或 ...

  7. 【腾讯Bugly干货分享】那些年,我们一起写过的“单例模式”

    题记 度娘上对设计模式(Design pattern)的定义是:"一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结."它由著名的"四人帮",又称 ...

  8. Android设计模式之——单例模式

    一.介绍 单例模式是应用最广的模式之一,也可能是很多初级工程师唯一会使用的设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在.许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系 ...

  9. 设计模式(一)单例模式的七种写法

    1. 饿汉模式 public class Singleton { private static Singleton instance = new Singleton(); private Single ...

最新文章

  1. 零基础小白学Java难度大不大
  2. Mule3用户手册:Mule ESB 3使用要点
  3. java访问远程共享文件
  4. ioc spring 上机案例_通过实例解析Spring Ioc项目实现过程
  5. VS生成Cordova for Android应用之Gradle
  6. 训练日志 2018.11.7
  7. JAVA之旅(五)——this,static,关键字,main函数,封装工具类,生成javadoc说明书,静态代码块...
  8. python 中 print 函数用法总结
  9. 中国买家团撑起2018芯片市场,华为千亿支出排名全球第三 | 盘点
  10. Expert 诊断优化系列------------------你的CPU高么?
  11. HTTP协议&SOCKET协议
  12. stem函数--Matplotlib
  13. 南阳oj-----懒省事的小明(set)
  14. 杀毒软件 McAfee 创始人狱中身亡,75 年传奇人生画下句号
  15. vs2017 中项目的publish,即“发布”到底是什么?
  16. 深度学习之数据标准化方法综述
  17. 【我的新颖社区社交产品架构构思设想】
  18. 0基础学绘画怎么临摹
  19. jq滚动小插件superslide2,的确是很强悍的
  20. 九宝老师公开课第1讲:微信公众平台与javaWeb的结合开发-CSDN公开课-专题视频课程...

热门文章

  1. 配置管理——配置管理委员会
  2. 如何使用谷歌插件自动备份标签
  3. 黑马程序员视频教程学习mybatis框架常用注释SQL语句学习笔记?
  4. 差分函数(差分运算)
  5. [JavaScript高级程序设计]JavaScript介绍
  6. symantec忘记了密码时如何卸载?
  7. 小程序消息推送(含源码)java实现小程序推送,springboot实现微信消息推送
  8. HTTP和TCP之间的关系
  9. 我的STM32 IAP BOOT跳转到APP进入HardFault_Handler解决方案
  10. LyX的一些使用问题收集