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

方案1

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

这种做法的错误是很明显的,如果两个线程分别调用getInstance,由于对共享变量的访问没有做同步,很容易出现下面两种情况:
1.线程A和B都看到instance没有初始化,于是分别进行了初始化。
2.instance=new Instance操作被重排序,实际执行过程可能是:先分配内存,然后赋值给instance,最后再执行初始化。
如果是这样的话,其他线程可能就会读取到尚未初始化完成的instance对象。

方案2

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

这种做法的问题是很明显的,每一次读取instance都需要同步,可能会对性能产生较大的影响。

方案3

方案3是一个错误的双重检测加锁实现,看代码:

public class UnsafeLazyInit{
private static Instance instance; public static Instance getInstance(){
if (instance == null){
synchronized(UnsafeLazyInit.classs){
if (instance == null){
instance = new Instance();
}
}
}
return instance;
}
}

这种方案看似解决了上面两种方案都存在的问题,但是也是有问题的。

问题根源

instance = new Instance();

这一条语句在实际执行中,可能会被拆分程三条语句,如下:

memory = allocate();
ctorInstance(memory); //2
instance = memory; //3

根据重排序规则,后两条语句不存在数据依赖,因此是可以进行重排序的。
重排序之后,就意味着,instance域在被赋值了之后,指向的对象可能尚未初始化完成,而instance域是一个静态域,
可以被其他线程读取到,那么其他线程就可以读取到尚未初始化完成的instance域。

基于volatile的解决方案

要解决这个办法,只需要禁止语句2和语句3进行重排序即可,因此可以使用volatile来修改instance就能做到了。

private volatile static Instance instance;

因为Volatile语义会禁止编译器将volatile写之前的操作重排序到volatile之后。

基于类初始化的解决方案

Java语言规范规定,对于每一个类或者接口C ,都有一个唯一的初始化锁LC与之对应,从C到LC的映射,由JVM实现。
每个线程在读取一个类的信息时,如果此类尚未初始化,则尝试获取LC去初始化,如果获取失败则等待其他线程释放LC。
如果能获取到LC,则要判断类的初始化状态,如果是位初始化,则要进行初始化。如果是正在初始化,
则要等待其他线程初始化完成,如果是已经初始化,则直接使用此类对象。

public class InstanceFactory {
private static class InstanceHolder {
public static Instance instance = new Instance();
} public static Instance getInstance() {
return InstanceHolder.instance ; //这里将导致InstanceHolder类被初始化
}
}

结论

字段延迟初始化降低了初始化类或者创建实例的开销,但是增加零访问被延迟促使化的字段的开销。
在大部分时候,正常的初始化要优于延迟初始化。如果确实需要对实例字段使用线程安全的延迟初始化,
请使用上面介绍的基于volatile的延迟初始化方案;如果确实需要对静态字段使用线程安全的延迟初始化,
请使用上面基于类初始化方案的延迟初始化。

JAVA 双重检查锁定和延迟初始化的更多相关文章

  1. volatile双重检查锁定与延迟初始化

    一.基本概念: 1.volatile是轻量级的synchronized,在多核处理器开发中保证了共享变量的“可见性”.可见性的意思是,当一个线程修改一个共享变量时,另一个线程能读到这个修改的值. 2. ...

  2. 双重检查锁定与延迟初始化(转自infoq)

    很好的文章,转自http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization 在java程序中,有 ...

  3. 从学习“单例模式”学到的Java知识:双重检查锁和延迟初始化

    一切真是有缘,上午刚刚看完单例模式,还在为其中的代码块同步而兴奋,下午就遇见这篇文章:双重检查锁定与延迟初始化.我一看,文章开头语出惊人,说这是一种错误的优化,我说,难道上午学的东西下午就过时了吗?仔 ...

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

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

  5. java 双重检查模式

    java 双重检查模式 在并发环境下 兼顾安全和效率 成例(Idiom)是一种代码层次上的模式,是在比设计模式的层次更具体的层次上的代码技巧.成例往往与编程语言密切相关.双重检查成例(Double C ...

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

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

  7. 利用双重检查锁定和CAS算法:解决并发下数据库的一致性问题

    背景 ​ 最近有一个场景遇到了数据库的并发问题.现在先由我来抽象一下,去掉不必要的繁杂业务. ​ 数据库表book存储着每本书的阅读量,一开始数据库是空的,不存在任何的数据.当用户访问接口的时候,判断 ...

  8. Singleton(单例)模式和Double-Checked Locking(双重检查锁定)模式

    问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...

  9. Singleton - 单例模式和Double-Checked Locking - 双重检查锁定模式

    问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...

随机推荐

  1. 微信小程序开发教程(九)视图层——.wxss详解

    WXSS是一套样式语言,用于描述WXML的组件样式. 官方文档表示,WXSS的选择器目前支持(“.class”.“#id”.“elemnt”.“element,element”.“::after”.“ ...

  2. 【线段树】bzoj3922 Karin的弹幕

    设置一个值K. d<=K:建立多组线段树:d>K:暴力. 最优时间复杂度的伪计算: O(n*K*logn(建树)+m*logn(询问类型1)+m*n/K(询问类型2)+m*K*logn(修 ...

  3. iOS 读取Json 代码

    保存一下iOS 读取Json的代码,留着以后Copy用,哈哈. NSString* path = [[NSBundle mainBundle] pathForResource: @"Sand ...

  4. select * from sys.sysprocesses

    MSDN:包含正在 SQL Server 实例上运行的进程的相关信息.这些进程可以是客户端进程或系统进程. 视图中主要的字段: 1. Spid:Sql Servr 会话ID 2. Kpid:Windo ...

  5. mysql-connector-java-6日期存储时差的问题解决方法

    在my.ini文件中的[mysqld]下面加入 default-time_zone = '+8:00' 重启mysql 修改driver连接路径 这样日期保存到mysql就是正确的了,但是mysql- ...

  6. WebLogic MBean Monitor

    weblogic server提供了一个dashboard让我们对mbean进行图形化的展现和分析,地址是 http://localhost:7001/console/dashboard 但有时候总是 ...

  7. Android--使用XMLPull解析xml

    在Android中极力推荐的xmlpull方式解析xml.xmlpull不只能够使用在Android上.相同也适用于javase,但在javase环境下.你须要自己去获取xmlpull所依赖的类库. ...

  8. openstack学习笔记(一)-openstack的基础知识

    一.OpenStack的基础知识 openstack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache2.0许可证(兼容GPLv3以及DFSG)授权的自由软件和 ...

  9. django admin中文输入编码错误

    修改models里面的str方法,改为unicode class Category(models.Model): name = models.CharField(max_length=20, verb ...

  10. Python数据整合与数据准备-BigGorilla实例应用

    参考文档:http://www.biggorilla.org/walkt/ 一.BigGorilla应用主要步骤 如下图: 二.实例应用 1.数据获取 urllib是非常受欢迎的用于在网络上读取数据的 ...