单例模式中的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. Web开发者的福利 30段超实用CSS代码

    1.花式连字符(&) 这个类应该在span元素里使用,并且里面包括&字符.它使用经典的serif字体和斜体来增强&符号. .amp { font-family: Baskerv ...

  2. Modbus Com SerialPort

    项目中用到的工具,串口modbus协议读写数据. public class ModbusHelper { private readonly SerialPort _serialPort; privat ...

  3. C# 工具类之数据库链接

     一.SQL Server 相关 /// <summary> /// 数据库的通用访问代码 /// 此类为抽象类, /// 不允许实例化,在应用时直接调用即可 /// </summa ...

  4. 小程序插入html代码

    转自:https://blog.csdn.net/BetterGG/article/details/81027291 一.首先,html 分 2 种情况: 1.我在做详情页开发的时候发现获取到的数据是 ...

  5. JSONObject与null

    前言 今天在写代码的时候发现在 JSON 中 由于put了key对应的value为null,结果这个JSON键值对没有输出 org.json.JSONObject 在orgJSON 中,如果直接put ...

  6. oracle数据库的安装与连接关键点

    一.window xp系统上安装Oracle Database 10G 解锁Scott.Hr账号并重置口令 远程连接数oracle数据库地址 二.在Mac系统上使用Navicat远程连接oracle数 ...

  7. SqlServer 2005升级至SqlServer 2008 解析Json 字符集问题

    如果你数据库是通过sqlserver 2008以上版本创建的请绕过: 客户以前用的是sqlserver2005 创建的数据库.后来升级到 sqlserver 2008 . 有个业务用到了json查询 ...

  8. springcloud 集成kafka问题记录,发消息报错:ERROR o.s.kafka.support.LoggingProducerListener - Exception thrown when sending a message with key='null' and payload='{-1,

    在springcloud集成kafka,发送消息时报错: 2018-08-15 16:01:34.159 [http-nio-8081-exec-1] INFO  org.apache.kafka.c ...

  9. [CTSC2008]祭祀(构造方案)

    前面的话 这道题显然就是最长反链 根据 \(Dilworth\) 定理:最小链覆盖数 = 最长反链长度 然后传递闭包跑匹配即可 \(luogu\)交了一下,\(WA\) 了 \(QAQ\) 本来各种 ...

  10. [转]运用@media实现网页自适应中的几个关键分辨率

    转自百度经验:http://jingyan.baidu.com/article/6f2f55a1ab36c3b5b83e6c46.html 经常为不同分辨率设备或不同窗口大小下布局错位而头疼,可以利用 ...