Java单例的常见形式

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

Java单例的常见形式

本文目的:总结Java中的单例模式

本文定位:学习笔记

学习过程记录,加深理解,便于回顾。也希望能给学习的同学一些灵感

一、非延迟加载单例类

public class Singleton {private Singleton() {}private static final Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}
}

二、同步延迟加载

public class Singleton {private static Singleton instance = null;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

三、双重检测同步延迟加载

为了减少同步的开销,于是有了双重检查模式

为处理原版非延迟加载方式瓶颈问题,我们需要对 instance 进行第二次检查,目的是避开过多的同步(因为这里的同步只需在第一次创建实例时才同步,一旦创建成功,以后获取实例时就不需要同获取锁了),但在Java中行不通,因为同步块外面的if (instance == null)可能看到已存在,但不完整的实例。

public class Singleton {private volatile static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized(Singleton.class) { // 1  if (instance == null) { // 2  instance = new Singleton(); // 3  }}}return instance;}
}

双重检测锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是失败的一个主要原因。

无序写入: 为解释该问题,需要重新考察上述清单中的 //3 行。此行代码创建了一个 Singleton 对象并初始化变量 instance 来引用此对象。这行代码的问题是:在 Singleton 构造函数体执行之前,变量 instance 可能成为非 null 的,即赋值语句在对象实例化之前调用,此时别的线程得到的是一个还会初始化的对象,这样会导致系统崩溃。 什么?这一说法可能让您始料未及,但事实确实如此。在解释这个现象如何发生前,请先暂时接受这一事实,我们先来考察一下双重检查锁定是如何被破坏的。假设代码执行以下事件序列:

  1. 线程 1 进入 getInstance() 方法。
  2. 由于 instance 为 null,线程 1 在 //1 处进入 synchronized 块。
  3. 线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非 null。
  4. 线程 1 被线程 2 预占。
  5. 线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将 instance 引用返回给一个构造完整但部分初始化了的 Singleton 对象。
  6. 线程 2 被线程 1 预占。
  7. 线程 1 通过运行 Singleton 对象的构造函数并将引用返回给它,来完成对该对象的初始化。

为展示此事件的发生情况,假设代码行 instance =new Singleton(); 执行了下列伪代码:

mem = allocate();             //为单例对象分配内存空间.
instance = mem;               //注意,instance 引用现在是非空,但还未初始化
ctorSingleton(instance);    //为单例对象通过instance调用构造函数

这段伪代码不仅是可能的,而且是一些 JIT 编译器上真实发生的。执行的顺序是颠倒的,但鉴于当前的内存模型,这也是允许发生的。JIT 编译器的这一行为使双重检查锁定的问题只不过是一次学术实践而已。

在 Java 中双重检查模式无效的原因是在不同步的情况下引用类型不是线程安全的。对于除了 long 和 double 的基本类型,双重检查模式是适用 的。比如下面这段代码就是正确的:

private int count;  
public int getCount(){  if (count == 0){   synchronized(this){   if (count == 0){  count = computeCount();  //一个耗时的计算  }     }    }  return count;  
} 

上面就是关于java中双重检查模式(double-check idiom)的一般结论。

double-check无效原因可参见

但是事情还没有结束,因为java的内存模式也在改进中。Doug Lea 在他的文章中写道:“根据最新的 JSR133 的 Java 内存模型,如果将引用类型声明为 volatile,双重检查模式就可以工作了”, 参见 。

Section 2.2.7.x on the Memory Model doesn't provide current details of the JSR133 spec revision. (The basic ideas still apply though.) One change that commonly arises in practice is that Section 2.2.7.4 and 2.4.1.2 should say that reading a volatile reference makes visible other changes to the referred object by the thread writing the reference. In particular, double-check idioms work in the expected way when references are declared volatile.

所以以后要在 Java 中使用双重检查模式,可以使用下面的代码:

private volatile Resource resource;  
public Resource getResource(){  if (resource == null){   synchronized(this){   if (resource==null){  resource = new Resource();    }     }    }  return resource;  
}  

四、使用内部类实现延迟加载(推荐)

推荐方法Initialization-on-demand holder idiom

public class Singleton {  static class SingletonHolder {  static Singleton instance = new Singleton();  }  public static Singleton getInstance(){  return SingletonHolder.instance;  }  
} 

其他

以上为常见单例形式,另有 ThreadLocal、枚举 等实现形式

此处不作介绍

参考

  • http://www.cnblogs.com/dolphin0520/p/3920373.html#!comments
  • http://www.iteye.com/topic/652440
  • http://www.iteye.com/topic/260515
  • https://blog.csdn.net/dl88250/article/details/5439024
  • https://blog.csdn.net/chenchaofuck1/article/details/51702129

对本文有什么建议(内容、写作风格等),欢迎留言提出,感谢!

转载于:https://my.oschina.net/lichuangnk/blog/1856430

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/254095.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

U-Boot启动过程完全分析

1.1 U-Boot 工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 硬件设备初始化 加载U-Boot第二阶段代码到RAM空间 设置好栈 跳转到第二阶段代码入口 (2&#x…

CJOJ 2171 火车站开饭店(树型动态规划)

CJOJ 2171 火车站开饭店(树型动态规划) Description 政府邀请了你在火车站开饭店,但不允许同时在两个相连的火车站开。任意两个火车站有且只有一条路径,每个火车站最多有 50 个和它相连接的火车站。 告诉你每个火车站的利润&#…

各视频、各音频之间格式任意玩弄(图文详解)

写在前面说的话 在这里,记录下来,是为了方便以后偶尔所制作所需和你们前来的浏览学习。 学会,玩弄一些视频和音频的软件,只有好处没有害处。同时,也不需很多时间,练练手罢了。也是方便自己所用吧&#xff0…

oracle 如何查看日志?

2019独角兽企业重金招聘Python工程师标准>>> Oracle日志查看一.Oracle日志的路径:登录:sqlplus "/as sysdba"查看路径:SQL> select * from v$logfile;SQL> select * from v$logfile;(#日志文件路径)二…

回归_英国酒精和香烟关系

sklearn实战-乳腺癌细胞数据挖掘(博客主亲自录制视频教程) https://study.163.com/course/introduction.htm?courseId1005269003&utm_campaigncommission&utm_sourcecp-400000000398149&utm_mediumshare 数据统计分析联系:QQ:&a…

【转】如何用Maven创建web项目(具体步骤)

使用eclipse插件创建一个web project 首先创建一个Maven的Project如下图 我们勾选上Create a simple project (不使用骨架) 这里的Packing 选择 war的形式 由于packing是war包,那么下面也就多出了webapp的目录 由于我们的项目要使用eclipse发…

可能是目前最详细的Redis内存模型及应用解读

Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时,会接触Redis的5种对象类型:字符串、哈希、列表、集合、有序集合。丰富…

POJ 1696 Space Ant 极角排序(叉积的应用)

题目大意:给出n个点的编号和坐标,按逆时针方向连接着n个点,按连接的先后顺序输出每个点的编号。 题目思路:Cross(a,b)表示a,b的叉积,若小于0:a在b的逆时针方向,若大于0a在…

SuperMap iDesktop之导入数据

SuperMap作为一个平台软件有自己的数据格式,现要将ESRI的SHP数据导入到SuperMap的udb数据库中,可以完成导入,但也不得不说几点问题。 下面是ArcGIS中批量导入SHP的操作界面。 比较分析 (1)界面简洁性 明显ArcGIS要简洁…

MyBatis 实践 -配置

MyBatis 实践标签: Java与存储 Configuration mybatis-configuration.xml是MyBatis的全局配置文件(文件名称随意),其配置内容和顺序例如以下: properties : 属性(文件)载入/配置settings : 全局配置參数typeAliases : 定义类型别名typeHandlers : 类型处理器objectF…

DM365视频处理流程/DM368 NAND Flash启动揭秘

DM365的视频处理涉及到三个相关处理器,分别是视频采集芯片、ARM处理器和视频图像协处理器(VICP),整个处理流程由ARM核协调。视频处理主要涉及三个处理流程,分别是视频采集、视频编码和对编码后的视频的处理&#xff0c…

系统的Drawable(四)-LayerListDrawable

系统的Drawable(四)-LayerListDrawable 学习自 https://blog.csdn.net/u014695188/article/details/52815444 LayerListDrawable 漫谈 使用layer-list可以将多个drawable按照顺序层叠在一起显示,默认情况下,所有的item中的drawable都会自动根据它附上vie…

图像处理:镜头频率(衍射极限) 和 相机采样:显微镜的采样定理

采样定理大家都知道,相信不用多说。 我自己写下来给自己看。 下面,我总结 大家平时照相的镜头或者显微镜的物镜的情况下: 采样频率是指图像在数字化的时候的过程,实际上就是我们相机感光元件CCD或者CMOS的一个个小像元把模拟的连续…

像素越多越好?像元的面积越小越好?为何底大一级压死人?

像素越多越好?像素点的面积越小越好?为何底大一级压死人? 像素是:图像最小单元的数量,例如6000*4000,像素数量就是24*10^6。 像素太少当然图像就看不见了,看不清晰了。 但是现在几乎所有手机和相…

DM6446开发攻略:V4L2视频驱动和应用分析

针对DAVINCI DM6446平台,网络上也有很多网友写了V4L2的驱动,但只是解析Montavistalinux-2.6.10 V4L2的原理、结构和函数,深度不够。本文决定把Montavista 的Linux-2.6.18 V4L2好好分析一下,顺便讲解在产品中的应用,满足…

相机像素尺寸(像元大小)和成像系统分辨率之间的关系

相机像素尺寸(像元大小)和成像系统分辨率之间的关系 在显微成像系统中,常常会用分辨率来评价其成像能力的好坏。这里的分辨率通常是指光学系统的极限分辨率以及成像探测器的图像分辨率。最终图像所呈现出的实际分辨率,取决于二者的…

工业相机之全局曝光与卷帘曝光

曝光方式包括两种: 全局曝光(global shutter)卷帘曝光(rolling shutter) CCD相机都是全局曝光,CMOS相机既有全局曝光也有卷帘曝光 全局曝光 全局曝光的方式比较简单。也就是说光圈打开后,整个图…

.NET 环境中使用RabbitMQ

在企业应用系统领域,会面对不同系统之间的通信、集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要。其次,系统中一般会有很多对实时性要求不高的但是执行起来比较较耗时的地方,比如发送短信&#…

成像质量、像素个数、感光元件尺寸的关系

成像质量、像素个数、感光元件尺寸的关系 感光元件 (影像传感器) 就是拍摄的照片最终成像的位置。相当于传统相机里面的胶卷,不同相机的感光元件尺寸是不一样的。 1. 像素的含义 两个 100 平方米的房子 A 和 B,A 房子里面平均分成 10 个房间&#xff…

15、iOS开发之duplicate symbols for architecture x86_64错误

1. 错误提示 2. 分析错误原因 3. 解决问题办法 一、错误提示 在我们写代码过程中可能会经常遇到这样一个错误&#xff1a; [objc] view plaincopy print?<span style"font-size:32px;color:#ff0000;">ld: 4 duplicate symbols for architecture x86_64 clang…