volatile禁止重排使用场景与单例模式的Double Check Lock
普通单例模式Demo
public class Demo{
private static Demo INSTANCE;
private Demo(){}
public static Demo getInstance(){
if(INSTANCE==null){
// 饿汉式单例
INSTANCE=new Demo();
}
return INSTANCE;
}
}
上面单例实现方式在单线程访问下没有问题,但是在并发访问时,会产生多个对象。
如程序启动 A线程获取INSTANCE执行完if判断为null后,线程B获取到CPU执行权并完成if判断和INSTANCE实例化得到时列对象,当A线程再次获得执行权后执行if中语句,又会创建一个新对象并对INSTANCE赋值,此时A,B得到的为两个不同的对象。
多线程版单列模式
public class Demo{
private static Demo INSTANCE;
private Demo(){}
public static synchronized Demo getInstance(){
if(INSTANCE==null){
INSTANCE=new Demo();
}
return INSTANCE;
}
}
使用synchronized关键字加锁(锁对象是Demo.class对象),保证了每次只会有一个线程执行方法。
虽然上述程序已不会出现使用问题,但是直接对整个方法进行锁定太过粗暴,作为高雅人士应该使用更优雅的方式实现锁定。
优化多线程版单列模式
public class Demo{
private static Demo INSTANCE;
private Demo(){}
public static Demo getInstance(){
if(INSTANCE==null){
synchronized (Demo.class){
// 锁细化
if(INSTANCE==null){
// 双重检查避免再次创建对象
INSTANCE=new Demo();
}
}
}
return INSTANCE;
}
}
对要逐个执行的功能锁定,对锁细化节省同步时间提高执行效率。
此时功能已经接近完美,但是对于超高并发的任务来说并不安全
如INSTANCE=new Demo()这句代码,编译后将会拆分出多个指令(以下只取三个关键指令描述)
- 初始化对象成员变量并赋默认值
- 将为我们要赋值给成员变量的值替换掉默认值
- 将对象的地址赋值给对应的引用
由于CPU存在指令重排的优化机制,在多线程访问时可能会影响到我们的执行结果,如下将2,3指令掉个顺序
- 初始化对象成员变量并赋默认值
- 将对象的地址赋值给对应的引用
- 将为我们要赋值给成员变量的值替换掉默认值
此时,按照我们优化过的单列模式进行超高并发任务时可能会出现意想不到的结果。
如:线程A执行,获取到锁对象进行INSTANCE=new Demo()对对象进行初始化,执行1,2后此时INSTANCE已经有指向的对象,但是对象的初始化还未完成,然后线程B执行进行第一次if(INSTANCE==null)判断,此时INSTANCE非null,获得对象,那么在线程A对对象默认值替换的3操作未完成时,线程B对INSTANCE对象的成员变量进行的操作都是存在问题的。
多线程版单列模式终极版
public class Demo{
private static volatile Demo INSTANCE; // 使用volatile 关键字禁止对指令进行重排
private Demo(){}
public static Demo getInstance(){
if(INSTANCE==null){
synchronized (Demo.class){
// 锁细化
if(INSTANCE==null){
// 双重检查避免再次创建对象
INSTANCE=new Demo();
}
}
}
return INSTANCE;
}
}
使用volatile修饰INSTANCE对象,那么CPU对该对象的操作将不会再进行指令重排,确保了对象初始化完成,并最后一步返回对象的地址给引用,达到万无一失的情况。
volatile禁止重排使用场景与单例模式的Double Check Lock的更多相关文章
- 对volatile的理解--从JMM以及单例模式剖析
请谈谈你对volatile的理解 1.volitale是Java虚拟机提供的一种轻量级的同步机制 三大特性1.1保证可见性 1.2不保证原子性 1.3禁止指令重排 首先保证可见性 1.1 可见性 概念 ...
- 【并发编程】Volatile原理和使用场景解析
目录 一个简单列子 Java内存模型 缓存不一致问题 并发编程中的"三性" 使用volatile来解决共享变量可见性 volatile和指令重排(有序性) volatile和原子性 ...
- Volatile禁止指令重排序(三)
Volatile禁止指令重排 计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令重排,一般分为以下三种: 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系 ...
- 单例模式+volatile禁止指令重排序
单例模式: 单例,顾名思义就是只能有一个.不能再出现第二个.就如同地球上没有两片一模一样的树叶一样. 在这里就是说:一个类只能有一个实例,并且整个项目系统都能访问该实例. 单例模式共分为两大类: 懒汉 ...
- volatile原理和应用场景
volatile是java语言中的一个关键字,常用于并发编程,有两个重要的特点:具有可见性,java虚拟机实现会为其满足Happens before原则;不具备原子性.用法是修饰变量,如:volati ...
- 单例模式的两种实现方式对比:DCL (double check idiom)双重检查 和 lazy initialization holder class(静态内部类)
首先这两种方式都是延迟初始化机制,就是当要用到的时候再去初始化. 但是Effective Java书中说过:除非绝对必要,否则就不要这么做. 1. DCL (double checked lockin ...
- 由单例模式学到:lock锁
lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁. lock (xxx) { // Critical code section. } lock 关键字可确保当一 ...
- double check 解决单例模式的多线程并发问题
最近被多线程问题(multi-thread issue)弄昏了头.以前虽然也知道系统里要考虑多线程问题,也无数次见到double-check的代码,但是由于自己碰到这方面的问题基本上就是从其他地方 ...
- Java程序员面试必备:Volatile全方位解析
前言 volatile是Java程序员必备的基础,也是面试官非常喜欢问的一个话题,本文跟大家一起开启vlatile学习之旅,如果有不正确的地方,也麻烦大家指出哈,一起相互学习~ 1.volatile的 ...
随机推荐
- 【轻松学编程】如何快速学会一门高级编程语言,以python为例
python文章目录 关注公众号"轻松学编程"了解更多. 写在前面:如何快速(比如在一个月内)学会一门高级编程语言? 现在想学一门编程语言并不难,网上有很多资料,包括书籍.博客.视 ...
- (五)F5和CTRL+F5两种刷新的区别
一.刷新原理不同: F5触发的HTTP请求的请求头中通常包含了If-Modified-Since 或 If-None-Match字段,或者两者兼有. CTRL+F5触发的HTTP请求的请求头中没有上面 ...
- Spring Security 实战干货:客户端OAuth2授权请求的入口
1. 前言 在Spring Security 实战干货:OAuth2第三方授权初体验一文中我先对OAuth2.0涉及的一些常用概念进行介绍,然后直接通过一个DEMO来让大家切身感受了OAuth2.0第 ...
- WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一)
WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一) 很多小伙伴在web开发中都遇倒的问题? JavaWeb项目注销后,可能存在通过浏览器缓存回退的方式进入主页系统 WEB项目 ...
- maven install 时 pom中skip test
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-suref ...
- 企业网络拓扑VRRP主备功能实例(一)
组网图形 VRRP主备备份简介 通常,同一网段内的所有主机上都存在一条相同的.以网关为下一跳的缺省路由.主机发往其他网段的报文将通过缺省路由发往网关,再由网关进行转发,从而实现主机与外部网络的通信. ...
- ceph在centos7下一个不容易发现的改变
在centos6以及以前的osd版本,在启动osd的时候,回去根据ceph.conf的配置文件进行挂载osd,然后进行进程的启动,这个格式是这样的 [osd.0] host = hostname de ...
- Idea eclipse 快捷键版
查找/搜索 打开搜索界面 Ctrl+H 查找类文件 Ctrl+Shift+T 最近访问上一个文件 Ctrl+Alt+ ← 最近访问下一个文件 C ...
- Apache POI读写Excel
Apache POI是Apache软件基金会的开放源码函式库,POIAPI给Java程序对Microsoft Office格式档案读和写的功能. 官方文档 [https://poi.apache.or ...
- redis部署安装【建议收藏】
一.redis安装教程 1.安装redis ~]# yum -y install gcc gcc-c++ make ~]# tar -xf redis-4.0.8.tar.gz ~]# cd redi ...