JAVA 双重检查锁定和延迟初始化
双重检查锁定的由来
在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 双重检查锁定和延迟初始化的更多相关文章
- volatile双重检查锁定与延迟初始化
一.基本概念: 1.volatile是轻量级的synchronized,在多核处理器开发中保证了共享变量的“可见性”.可见性的意思是,当一个线程修改一个共享变量时,另一个线程能读到这个修改的值. 2. ...
- 双重检查锁定与延迟初始化(转自infoq)
很好的文章,转自http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization 在java程序中,有 ...
- 从学习“单例模式”学到的Java知识:双重检查锁和延迟初始化
一切真是有缘,上午刚刚看完单例模式,还在为其中的代码块同步而兴奋,下午就遇见这篇文章:双重检查锁定与延迟初始化.我一看,文章开头语出惊人,说这是一种错误的优化,我说,难道上午学的东西下午就过时了吗?仔 ...
- Java盲点:双重检查锁定及单例模式
尊重原创: http://gstarwd.iteye.com/blog/692937 2004 年 5 月 01 日 所有的编程语言都有一些共用的习语.了解和使用一些习语很有用,程序员们花费宝贵的时间 ...
- java 双重检查模式
java 双重检查模式 在并发环境下 兼顾安全和效率 成例(Idiom)是一种代码层次上的模式,是在比设计模式的层次更具体的层次上的代码技巧.成例往往与编程语言密切相关.双重检查成例(Double C ...
- DCL,即Double Check Lock,中卫双重检查锁定。
DCL,即Double Check Lock,中卫双重检查锁定. [Java并发编程]之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码) 关于单例.关于DCL: ...
- 利用双重检查锁定和CAS算法:解决并发下数据库的一致性问题
背景 最近有一个场景遇到了数据库的并发问题.现在先由我来抽象一下,去掉不必要的繁杂业务. 数据库表book存储着每本书的阅读量,一开始数据库是空的,不存在任何的数据.当用户访问接口的时候,判断 ...
- Singleton(单例)模式和Double-Checked Locking(双重检查锁定)模式
问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...
- Singleton - 单例模式和Double-Checked Locking - 双重检查锁定模式
问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...
随机推荐
- [CF235E]Number Challenge
$\newcommand{fl}[1]{\left\lfloor#1\right\rfloor}$题意:求$\sum\limits_{i=1}^a\sum\limits_{j=1}^b\sum\lim ...
- 【分块】【链表】bzoj2738 矩阵乘法
http://www.cnblogs.com/jianglangcaijin/p/3460012.html 首先将矩阵的数字排序.设置size,每次将size个数字插入.插入时,我们用h[i][j]记 ...
- 金融应用,计算将来的学费 Exercise05_07
/** * @author 冰樱梦 * 时间:2018年下半年 * 题目:金融应用,计算将来的学费 * */ public class Exercise05_07 { public static vo ...
- Spring中@Value用法收集
一.配置方式 @Value需要参数,这里参数可以是两种形式: @Value("#{configProperties['t1.msgname']}") 或者 @Value(" ...
- iOS开发笔记_4自定义TabBar
新博客:http://www.liuchendi.com 好多APP都使用的是自定义的TabBar,那这个功能应该如何实现呢?首先应该解决的问题就是,加载NavigationController的时候 ...
- 搭建redis集群环境
Redis的集群机制 ============================= 转自http://lib.csdn.net/article/redis/39999 别人写的,写得不错,转了. Red ...
- django admin中文输入编码错误
修改models里面的str方法,改为unicode class Category(models.Model): name = models.CharField(max_length=20, verb ...
- Kafka 简单实验二(Python实现简单生产者消费者)
Apache Kafka 是什么? Kafka 是一个开源的分布式流处理平台,其简化了不同数据系统的集成.流指的是一个数据管道,应用能够通过流不断地接收数据.Kafka 作为流处理系统主要有两个用处: ...
- EffectiveJava(25)泛型和数组的使用取舍及规范
泛型和数组 泛型:1.泛型是不可变的.对于任意两个不同类型Type1,type2;List既不是List的子类型,也不是List的超类型 2.泛型是通过擦除来实现的.故泛型只在编译时强化它们的信息,并 ...
- Centos 7 修改yum源为阿里源
因为官方的yum源在国内访问效果不佳, 需要改为国内比较好的阿里云或者网易的yum源, 具体修改步骤如下: cd /etc/yum.repos.d # 备份旧的配置文件 mv CentOS-Base. ...