volatile禁止重排使用场景与单例模式的Double Check Lock
2024-10-10 09:10:18
普通单例模式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对该对象的操作将不会再进行指令重排,确保了对象初始化完成,并最后一步返回对象的地址给引用,达到万无一失的情况。
最新文章
- 百度SDK的使用第一天
- github仓库的克隆、修改、上传方法(图)
- The Guide To Understanding mysqlreport
- IntelliJ IDEA 15 显示工具栏及底部周边工具栏
- 5.中文问题(自身,操作系统级别,应用软件的本身),mysql数据库备份
- 策略模式(Stratety)
- python复杂网络库networkx:算法
- debian apt sources
- 关于空想X
- 使用datagrip链接mysql数据库的报错问题.
- build配置项中maven常用插件
- Intellij Idea 解决字符乱码、设定颜色主题、字体
- ssm项目中KindEditor的图片上传插件,浏览器兼容性问题
- 第二次作业 对VC++6.0编译软件的评价
- Android Studio Prettify 插件
- win10图片打开方式里没有默认照片查看器的解决方法
- JS将时间戳转化为时间
- PHP数组缓存:三种方式JSON、序列化和var_export的比较
- Smallest Difference(暴力全排列)
- thymeleaf 学习笔记-基础篇(中文教程)