volatile双重检查锁定与延迟初始化
一、基本概念:
1、volatile是轻量级的synchronized,在多核处理器开发中保证了共享变量的“可见性”。可见性的意思是,当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。
2、volatile在修饰共享变量进行写操作时,在多核处理器下会引发两件事情:
1)将当前处理器缓存行的数据写回到系统内存。
2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。
3、在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议。每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,
当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态。当处理器对这个数据进行修改操作的时候,会重新从系统内存
中把数据读到处理器缓存里。
4、锁的happens-before规则保证释放锁和获取锁两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量的最后
写入。即使是64位的long型和double型变量,只要它是volatile变量,对该变量的读/写就有原子性。如果是多个volatile操作或类似于volatile++这种复合操作,
这些操作整体上不具有原子性。简而言之:
1)可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量的最后写入。
2)原子性。对任意一个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。
5、volatile写-读内存语意:
写语意:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
读语意:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量。
二、volatile在双重检查锁定与延迟初始化中的应用
在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销。双重检查锁定是常见的初始化技术。先看个双重检查锁定和延迟初始化的例子。
public class Singleton { private Singleton(){} private static Singleton singleton = null; public static Singleton getSafe2Instance() {
if(singleton == null) { //①第一次检查
synchronized (Singleton.class) { //②加锁
if(singleton == null) { //③第二次检查
singleton = new Singleton(); //④问题出现的地方
}
}
}
return singleton;
}
}
上边的代码有个问题,当线程执行到①处,发现singleton不为空,但是singleton引用的对象有可能还没有完成初始化。那线程获取到的singleton的引用就有可能是空的,
导致程序出错。为什么会出现线程获取到的singleton的引用时空的呢?我们看一下问题的根源。线程执行到④处,创建了一个对象,这一行代码可以分解为如下三行伪代码。
memory = allocate(); //1:分配对象内存空间
ctorInstance(memory); //2:初始化对象
singleton = memroy; //3:设置singleton指向刚分配的内存地址
第2行和第3行伪代码在编译器里可能会重排序。重排序后的伪代码为:
memory = allocate(); //1:分配对象内存空间
singleton = memroy; //3:设置singleton指向刚分配的内存地址
ctorInstance(memory); //2:初始化对象
如果发生重排序,另一个并发执行的线程B就有可能在①处判断instance不为null,线程B接下来将访问instance锁引用的对象,但此时这个对象可能还没有被线程A初始化。
那怎么解决这个问题呢?
1)不允许2和3重排序
2)允许2和3重排序,但不允许其他线程“看到”这个重排序。
针对第一条我们可以用volatile来解决,因为volatile有防止重排序的能力。
public class Singleton { private Singleton(){} private volatile static Singleton singleton = null; public static Singleton getSafe2Instance() {
if(singleton == null) {
synchronized (Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
针对第二条,我们可以记录类初始化的解决方案。因为JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,
JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。基于这个特性可以实现另一种线程安全的延迟初始化方案(这个方案被称之为Initialization On Demand Holder idion)
public class Singleton { private Singleton(){} private volatile static Singleton singleton = null; public static final Singleton getSafe3Instance() {
return LazyHolder.INSTANCE;
} private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
} }
参考:
[1]《Java并发编程艺术》,方腾飞
{2}《Java高并发程序设计》,葛一鸣
volatile双重检查锁定与延迟初始化的更多相关文章
- 双重检查锁定与延迟初始化(转自infoq)
很好的文章,转自http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization 在java程序中,有 ...
- JAVA 双重检查锁定和延迟初始化
双重检查锁定的由来在Java程序中,有时需要推迟一些高开销的对象的初始化操作,并且只有在真正使用到这个对象的时候,才进行初始化,此时,就需要延迟初始化技术.延迟初始化的正确实现是需要一些技巧的,否则容 ...
- 从学习“单例模式”学到的Java知识:双重检查锁和延迟初始化
一切真是有缘,上午刚刚看完单例模式,还在为其中的代码块同步而兴奋,下午就遇见这篇文章:双重检查锁定与延迟初始化.我一看,文章开头语出惊人,说这是一种错误的优化,我说,难道上午学的东西下午就过时了吗?仔 ...
- Java盲点:双重检查锁定及单例模式
尊重原创: http://gstarwd.iteye.com/blog/692937 2004 年 5 月 01 日 所有的编程语言都有一些共用的习语.了解和使用一些习语很有用,程序员们花费宝贵的时间 ...
- DCL,即Double Check Lock,中卫双重检查锁定。
DCL,即Double Check Lock,中卫双重检查锁定. [Java并发编程]之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码) 关于单例.关于DCL: ...
- 利用双重检查锁定和CAS算法:解决并发下数据库的一致性问题
背景 最近有一个场景遇到了数据库的并发问题.现在先由我来抽象一下,去掉不必要的繁杂业务. 数据库表book存储着每本书的阅读量,一开始数据库是空的,不存在任何的数据.当用户访问接口的时候,判断 ...
- Singleton(单例)模式和Double-Checked Locking(双重检查锁定)模式
问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...
- Singleton - 单例模式和Double-Checked Locking - 双重检查锁定模式
问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...
- 单例模式中用volatile和synchronized来满足双重检查锁机制
背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1 ...
随机推荐
- 总结一下《vue的使用》
1.用vue创建项目的时候, 1.安装axios,对axios进行处理,创建axios.js文件,设置基础请求地址, 设置前置守卫和独享守卫,对请求数据进行设置,(特别实在进行token验证的时候特别 ...
- 学以致用十八-----shell脚本之基础概念及变量
1.脚本脚本,说了很多年的脚本,一直都没怎么弄明白为什么叫脚本,还仅仅是script翻译过来的?今天再查看翻译,查阅了资料,对脚本有了个新的认识. script也叫剧本,脚本---剧本,像剧本一样,让 ...
- 学以致用七---Centos7.2+python3.6.2+django2.1.1 --搭建一个网站(补充)
补充:上一节出现的报错提示 可在settings.py 里,改成 ‘*’ ,这样所有的主机都可以访问了. 打开网页 注意红色框出来的 hello 是和 urls.py里的hello对应 urls.p ...
- python计算机硬件基础以及变量常量常量池,解释器编译器比较,python的两种运行方式
1.什么是编程语言 语言是一个事物与另外一个事物沟通的介质 编程语言是程序员与计算机沟通的介质 2.什么是编程 编程就是程序按照某种编程语言的语法规范将自己想要让计算机做的事情表达出来 表达的结果就是 ...
- (转)Memcache内存分配策略
转自:http://hi.baidu.com/software_one/item/0a0a6712dc7a319899ce33e0 一.Memcache内存分配机制 关于这个机制网上有很多解释的,我个 ...
- 合理提升WEB前端性能
前端的优化包括四个部分:HTML结构优化.CSS样式优化.JS行为优化.服务器的优化.合理的前端优化不仅能够提升网站加载速度,而且能够更好的提升用户体验和团队开发效率.所以前端性能优化的重要性是不言而 ...
- POJ1468 Sorting Slides
Sorting Slides Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 4442 Accepted: 1757 De ...
- common.php
<?php /** * */ class Common { if(!function_exists('is_php')) { function is_php($version = '5.0.0' ...
- string转Date转回String(JAVA)
String dateString = "2012-12-06 "; SimpleDateFormat sdf = new SimpleDateFormat("yyy ...
- 缓存中使用的ReentrantReadWriteLock锁
java中提供了lock锁,简便了设计缓存,下面程序主要是使用读写锁的应用.... import java.util.HashMap; import java.util.Map; import jav ...