单例模式中的volatile关键字

在之前学习了单例模式在多线程下的设计,疑惑为何要加volatile关键字。加与不加有什么区别呢?这里我们就来研究一下。单例模式的设计可以参考个人总结的这篇文章

  背景:在早期的JVM中,synchronized存在巨大的性能开销。因此,有人想出了一个“聪明”的技巧:双重检查锁定(Double-Checked Locking)。人们想通过双重检查锁定来降低同步的开销。下面是使用双重检查锁定来实现延迟初始化的示例代码。

public class DoubleCheckedLocking { // 1
private static Instance instance; // 2
public static Instance getInstance() { // 3
if (instance == null) { // 4:第一次检查
synchronized (DoubleCheckedLocking.class) { // 5:加锁
if (instance == null) // 6:第二次检查
instance = new Instance(); // 7:问题的根源出在这里
} // 8
} // 9
return instance; // 10
} // 11
}

上述的Instance类变量是没有用volatile关键字修饰的,会导致这样一个问题:

在线程执行到第4行的时候,代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化。

主要的原因是重排序。重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

第7行的代码创建了一个对象,这一行代码可以分解成3个操作:

memory = allocate();  // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory;  // 3:设置instance指向刚分配的内存地址

根源在于代码中的2和3之间,可能会被重排序。例如:

memory = allocate();  // 1:分配对象的内存空间
instance = memory;  // 3:设置instance指向刚分配的内存地址
// 注意,此时对象还没有被初始化!
ctorInstance(memory); // 2:初始化对象

这在单线程环境下是没有问题的,但在多线程环境下会出现问题:



B线程会看到一个还没有被初始化的对象。



A2和A3的重排序不影响线程A的最终结果,但会导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象。

所以只需要做一点小的修改(把instance声明为volatile型),就可以实现线程安全的延迟初始化。因为被volatile关键字修饰的变量是被禁止重排序的。

单例模式中的volatile关键字的更多相关文章

  1. Java中的volatile关键字的功能

    Java中的volatile关键字的功能 volatile是java中的一个类型修饰符.它是被设计用来修饰被不同线程访问和修改的变量.如果不加入volatile,基本上会导致这样的结果:要么无法编写多 ...

  2. zz剖析为什么在多核多线程程序中要慎用volatile关键字?

    [摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...

  3. java中的volatile关键字

    java中的volatile关键字 一个变量被声明为volatile类型,表示这个变量可能随时被其他线程改变,所以不能把它cache到线程内存(如寄存器)中. 一般情况下volatile不能代替syn ...

  4. 深入理解Java中的volatile关键字

    在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...

  5. java中的Volatile关键字使用

    文章目录 什么时候使用volatile Happens-Before java中的Volatile关键字使用 在本文中,我们会介绍java中的一个关键字volatile. volatile的中文意思是 ...

  6. Java中的volatile关键字为什么不是不具有原子性

    Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值是原子操作,为什么?为什么volatile能替代简单的锁,却不能保证原子性?这里面涉及volat ...

  7. 从一个小例子引发的Java内存可见性的简单思考和猜想以及DCL单例模式中的volatile的核心作用

    环境 OS Win10 CPU 4核8线程 IDE IntelliJ IDEA 2019.3 JDK 1.8 -server模式 场景 最初的代码 一个线程A根据flag的值执行死循环,另一个线程B只 ...

  8. 面试中的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的内容.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  9. C/C++中的volatile关键字

    volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据. 如果没有volatile关键字,则编译器可能优化读取和存 ...

随机推荐

  1. JAVA泛型——协变

    在上篇<JAVA泛型——基本使用>这篇文章中遗留以下问题,即将子类型Table或者也能添加到父类型Auction的泛型中,要实现这种功能必须借助于协变. 实验准备 现在在<JAVA泛 ...

  2. C# HttpHelper

    public enum HttpVerb { Get, Post } public class HttpHelper { private string _contentType = "app ...

  3. [转]wxParse-微信小程序富文本解析组件

    本文转自:https://github.com/icindy/wxParse 基本使用方法 Copy文件夹wxParse - wxParse/ -wxParse.js(必须存在) -html2json ...

  4. amazeui笔记-web组件

    Json.parse()

  5. H5+MUI上传文件

    应用场景:MUI+H5+WEBAPI 今天在给我外甥女调手机端上传图片的时候,发现他是直接调用的MUI下的api,直接调取相册或者相机,到最后只看见了一个文件的路径,所以以前写的上传文件就不太好套上去 ...

  6. Git——新手入门与上传项目到远程仓库GitHub

    Git:先进的分布式版本控制系统,一个开源式的分布式版本控制工具. Git安装 在Windows操作系统下,访问Git下载地址https://git-for-windows.github.io/ 注册 ...

  7. 八: 操作提示(wxml 即将废弃)

    首先需要注意的是 wxml的这些属性将要被废弃,不过可以看两眼.不愿意看的可以看下一章节同样是操作回馈只不过是js版的哦.   一.action-sheet 操作菜单 从屏幕底下出来菜单. 这里不用w ...

  8. 第9天:原型、继承、函数使用推荐以及this的指向

    原型 javascript原型指向改变如何添加方法和访问 <!DOCTYPE html> <html lang="en"> <head> < ...

  9. 基于Spark GraphX计算二度关系

    关系计算问题描述 二度关系是指用户与用户通过关注者为桥梁发现到的关注者之间的关系.目前微博通过二度关系实现了潜在用户的推荐.用户的一度关系包含了关注.好友两种类型,二度关系则得到关注的关注.关注的好友 ...

  10. Spring学习笔记:spring整合web之spring-web架包的引用(WebApplicationContextUtils注入容器)

    WebApplicationContextUtils 一.Spring整合web之前 案例:给部门列表添加新部门 import org.apache.log4j.Logger; import org. ...