1 前言

单例模式是我们经常使用的一种模式,一般来说很多资料都建议我们写成如下的模式:

/**

* Created by qiyei2015 on 2017/5/13.

*/

public class Instance {

private String str = "";

private int a = 0;

private static Instance ins = null;

/**

* 构造方法私有化

*/

private Instance(){

str = "hello";

a = 20;

}

/**

* DCL方式获取单例

* @return

*/

public static Instance getInstance(){

if (ins == null){

synchronized (Instance.class){

if (ins == null){

ins = new Instance();

}

}

}

return ins;

}

}

但是这种方式其实是有缺陷的,具体什么缺陷呢?我们首先要了解JVM了内存模型,请看下面分析

2 JVM内存模型

JVM模型如下图:

这里着重介绍下VM Stack,其他的我相信都比较熟悉。

VM Stack是线程私有的区域。他是java方法执行时的字典:它里面记录了局部变量表、 操作数栈、 动态链接、 方法出口等信息。

在《java虚拟机规范》一书中对这部分的描述如下:

栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接 (Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。

栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。

栈帧的存储空间分配在 Java 虚拟机栈( §2.5.5)之中,每一个栈帧都有自己的局部变量表( Local Variables, §2.6.1)、操作数栈( OperandStack, §2.6.2)和指向当前方法所属的类的运行时常量池( §2.5.5)的引用。

java中某个线程在访问堆中的线程共享变量时,为了加快访问速度,提升效率,会把该变量临时拷贝一份到自己的VM Stack中,并保持和堆中数据的同步。

3 传统DCL方式的缺陷

有了以上的基础知识我们就可以知道DCL方式的缺陷在哪儿了。当线程A在获取了Instance.class锁时,对ins进行 ins = new Instance() 初始化时,由于这是很多条指令,jvm可能会乱序执行。这个时候如果线程B在执行if (ins == null)时,正常情况下,如果为true,说明需要获取Instance.class锁,等待初始化。但是这时候,假设线程A再没有对ins进行初始化完,比如只对str进行了赋值,还没有来的及对a进行赋值,假如jvm将未完成赋值的值拷贝回堆中,这个时候线程B有可能读到的值就不是为null了,就会造成数据丢失的情况。这时候我们发现线程B获取的对象中a的值是0,而不是20

因为:对ins的写操作不 happen-before 对它的读操作

这就是DCL方式的缺陷,那么怎么避免呢?首先我们需要了解分析多线程的一大利器

4 happen-before原则

Happen-Before规则:

1 同一个线程中,书写在前面的操作happen-before书写在后面的操作。这条规则是说,在单线程 中操作间happen-before关系完全是由源代码的顺序决定的,这里的前提“在同一个线程中”是很重要的,这条规则也称为单线程规则 。这个规则多少说得有些简单了,考虑到控制结构和循环结构,书写在后面的操作可能happen-before书写在前面的操作,不过我想读者应该明白我的意思。

2 对锁的unlock操作happen-before后续的对同一个锁的lock操作。这里的“后续”指的是时间上的先后关系,unlock操作发生在退出同步块之后,lock操作发生在进入同步块之前。这是条最关键性的规则,线程安全性主要依赖于这条规则。但是仅仅是这条规则仍然不起任何作用,它必须和下面这条规则联合起来使用才显得意义重大。这里关键条件是必须对“同一个锁”的lock和unlock。

如果操作A happen-before操作B,操作B happen-before操作C,那么操作A happen-before操作C。这条规则也称为传递规

3 对volatile字段的写操作happen-before后续的对同一个字段的读操作.(Java5 新增)

4 单例模式的正确写法

有了以上的分析我们知道,我们只需要在保证对ins的访问是读在写之后即可,因此正确的做法是在ins 前加上一个关键字volatile。因此DCL的正确写法应该如下:

/**

* Created by qiyei2015 on 2017/5/13.

*/

public class Instance {

private String str = "";

private int a = 0;

private volatile static Instance ins = null;

/**

* 构造方法私有化

*/

private Instance(){

str = "hello";

a = 20;

}

/**

* DCL方式获取单例

* @return

*/

public static Instance getInstance(){

if (ins == null){

synchronized (Instance.class){

if (ins == null){

ins = new Instance();

}

}

}

return ins;

}

}

其实单例模式也有另一种我很喜欢的写法,那就是内部类:

/**

* Created by qiyei2015 on 2017/5/13.

*/

public class Instance {

/**

* 构造方法私有化

*/

private Instance(){

}

private static class SingleHolder{

private static final Instance ins = new Instance();

}

/**

* 内部类方式获取单例

* @return

*/

public static Instance getInstance(){

return SingleHolder.ins;

}

}

这种从jvm虚拟机上保证了单例,并且也是懒式加载。

java 单例 dcl_java 中单例模式DCL的缺陷及单例的正确写法相关推荐

  1. java中单例的应用_浅谈Java中单例模式的几种应用

    目录 浅谈Java中单例模式的几种应用 第一种:懒汉式 第二种:饿汉式 第三种:双重检索式 第四种:注册登记式 第五种:内部类形式 浅谈Java中单例模式的几种应用 日常开发中,为了提高我们系统中对象 ...

  2. java 单例 读写锁_终极锁实战:单JVM锁+分布式锁

    目录 1.前言 2.单JVM锁 3.分布式锁 4.总结 =========正文分割线================= 1.前言 锁就像一把钥匙,需要加锁的代码就像一个房间.出现互斥操作的典型场景:多 ...

  3. 单例模式不能被继承_Spring的单例实现原理

    单例模式有饿汉模式.懒汉模式.静态内部类.枚举等方式实现,但由于以上模式的构造方法是私有的,不可继承,Spring为实现单例类可继承,使用的是单例注册表的方式. 什么是单例注册表呢: Spring是通 ...

  4. python3的配置文件类单例实现_单例模式的几种实现方式及对比

    来源:博客园 作者:为何不是梦 链接:https://www.cnblogs.com/ibigboy/p/11423613.html 所谓单例就是在系统中只有一个该类的实例. 单例模式的核心分以下三个 ...

  5. Spring单例Bean与单例模式的区别

    Spring单例Bean与单例模式的区别在于它们关联的环境不一样,单例模式是指在一个JVM进程中仅有一个实例,而Spring单例是指一个Spring Bean容器(ApplicationContext ...

  6. Java查漏补缺(08)关键字:static、单例设计模式、理解main方法、类的成员之四:代码块、final关键字、抽象类、接口、内部类、枚举类、注解、包装类

    Java查漏补缺(08)关键字:static.单例设计模式.理解main方法.类的成员之四:代码块.final关键字.抽象类.接口.内部类.枚举类.注解.包装类 本章专题与脉络 1. 关键字:stat ...

  7. java中单例模式的3种实现

    1 饿汉式单例类.在类初始化时,已经自行实例化 class EagerSingleton {       private static final EagerSingleton m_instance ...

  8. java中单例模式用法详解

    最近空闲的时候回顾了一下常用的设计模式,其中单例模式是创建型模式中比较基础的一种设计模式,说起单例模式,想必大家并不模式,我们都知道的是,在单例模式下,能够保证一个类只有一个实例对象,就是说,外部访问 ...

  9. 以下哪个选项不是单例模式的优点_设计模式--单例

    概述 单例模式(SingletonPattern),保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式有 3 个特点: 单例类只有一个实例对象: 该单例对象必须由单例类自行创建: 单例类 ...

最新文章

  1. 在SAP BW中使用ABAP
  2. php项目自动布署mysql_如何自动化一键部署PHP项目
  3. c#大圣之路笔记——c# SqlDataReader和SqlDataAdapter区别
  4. sm4加密 解密(oc)
  5. 分布式技术比较(RPC,CORBA,WebService)
  6. python——变量的定义、命名
  7. raid卡组不同raid_RAID有哪几种?有什么区别?
  8. WIAC上,华为展区都有点儿啥?
  9. 分布式SOA基础架构崭露头角
  10. 4-1MapReduce概述
  11. 二次规划问题转换为半正定问题(QPtoSDP)
  12. rabbitMQ 常用api翻译
  13. 智能优化算法:头脑风暴优化算法-附代码
  14. J-Link软件和文档包的版本发行说明(1)[V3.00d ~ V4.94j版本]
  15. ASPUPload3.0注册机
  16. 统计工具代码同步安装和异步安装有何区别
  17. 用SpringBoot 做代web理服务器
  18. java 向路由器发送报文_9.IP选路 - loda0128的个人空间 - OSCHINA - 中文开源技术交流社区...
  19. html文字旋转以后变形,CSS3中的变形处理——transform功能(旋转、缩放、倾斜、移动)...
  20. 函数,类模板全特化,偏特化

热门文章

  1. Python实现三维切片mat文件保存某一张切片的图片
  2. spring 怎样判断数据是否变化_怎样判断辽砚是否是珍品?
  3. Python将Excel文件内容写入Word文件
  4. JavaEE基础第9章Java常用类
  5. 【项目管理】项目管理专业人员能力评价要求-国标
  6. 【推荐系统】短视频推荐系统概述
  7. MXNet gpu 版本快速安装(mxnet-cu101)
  8. 微信小程序 制作分享朋友圈的图片
  9. Cesium 几何体和外观(Geometry Appearances)
  10. 从零实现 USB_SLAVE读卡器 USB_MSC+FATFS+SD/SPI_FLASH/NANDFLASH