Volatile的应用

单例模式DCL代码

首先回顾一下,单线程下的单例模式代码

/**
* 单例模式
*
* @author xiaocheng
* @date 2020/4/22 9:19
*/
public class Singleton { private static Singleton singleton = null; private Singleton() {
System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
} public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
} public static void main(String[] args) {
System.out.println(Singleton.getInstance() == Singleton.getInstance());
System.out.println(Singleton.getInstance() == Singleton.getInstance());
System.out.println(Singleton.getInstance() == Singleton.getInstance());
System.out.println(Singleton.getInstance() == Singleton.getInstance());
}
}

最后输出的结果

但是在多线程的环境下,我们的单例模式是否还是同一个对象了

/**
* 单例模式
*
* @author xiaocheng
* @date 2020/4/22 9:19
*/
public class Singleton { private static Singleton singleton = null; private Singleton() {
System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
} public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
} public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
Singleton.getInstance();
}, String.valueOf(i)).start();
}
}
}

从下面的结果我们可以看出,我们通过SingletonDemo.getInstance() 获取到的对象,并不是同一个,而是被下面几个线程都进行了创建,那么在多线程环境下,单例模式如何保证呢?

解决方法1

引入synchronized关键字

    public synchronized static SingletonDemo getInstance() {
if(instance == null) {
instance = new SingletonDemo();
}
return instance;
}

输出结果

我们能够发现,通过引入Synchronized关键字,能够解决高并发环境下的单例模式问题

但是synchronized属于重量级的同步机制,它只允许一个线程同时访问获取实例的方法,但是为了保证数据一致性,而减低了并发性,因此采用的比较少

解决方法2

通过引入DCL Double Check Lock 双端检锁机制

就是在进来和出去的时候,进行检测

    public static SingletonDemo getInstance() {
if(instance == null) {
// 同步代码段的时候,进行检测
synchronized (SingletonDemo.class) {
if(instance == null) {
instance = new SingletonDemo();
}
}
}
return instance;
}

最后输出的结果为:

从输出结果来看,确实能够保证单例模式的正确性,但是上面的方法还是存在问题的

DCL(双端检锁)机制不一定是线程安全的,原因是有指令重排的存在,加入volatile可以禁止指令重排

原因是在某一个线程执行到第一次检测的时候,读取到 instance 不为null,instance的引用对象可能没有完成实例化。因为 instance = new SingletonDemo();可以分为以下三步进行完成:

  • memory = allocate(); // 1、分配对象内存空间
  • instance(memory); // 2、初始化对象
  • instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null

但是我们通过上面的三个步骤,能够发现,步骤2 和 步骤3之间不存在 数据依赖关系,而且无论重排前 还是重排后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。

  • memory = allocate(); // 1、分配对象内存空间
  • instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null,但是对象还没有初始化完成
  • instance(memory); // 2、初始化对象

这样就会造成什么问题呢?

也就是当我们执行到重排后的步骤2,试图获取instance的时候,会得到null,因为对象的初始化还没有完成,而是在重排后的步骤3才完成,因此执行单例模式的代码时候,就会重新在创建一个instance实例

指令重排只会保证串行语义的执行一致性(单线程),但并不会关系多线程间的语义一致性

所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,这就造成了线程安全的问题

所以需要引入volatile,来保证出现指令重排的问题,从而保证单例模式的线程安全性

private static volatile SingletonDemo instance = null;

最终代码

/**
* 单例模式
*
* @author xiaocheng
* @date 2020/4/22 9:19
*/
public class Singleton { private static volatile Singleton singleton = null; private Singleton() {
System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
} public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
} public static void main(String[] args) {
// System.out.println(Singleton.getInstance() == Singleton.getInstance());
// System.out.println(Singleton.getInstance() == Singleton.getInstance());
// System.out.println(Singleton.getInstance() == Singleton.getInstance());
// System.out.println(Singleton.getInstance() == Singleton.getInstance());
for (int i = 0; i < 10; i++) {
new Thread(() -> {
Singleton.getInstance();
}, String.valueOf(i)).start();
}
}
}

Volatile的应用DCL单例模式(四)的更多相关文章

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

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

  2. DCL单例模式

    我们第一次写的单例模式是下面这样的: public class Singleton { private static Singleton instance = null; public static ...

  3. DCL单例模式中的缺陷及单例模式的其他实现

    DCL:Double Check Lock ,意为双重检查锁.在单例模式中懒汉式中可以使用DCL来保证程序执行的效率. 1 public class SingletonDemo { 2 private ...

  4. Volatile关键字&&DCL单例模式,volatile 和 synchronized 的区别

    Volatile 英文翻译:易变的.可变的.不稳定的. 一.volatile 定义及用法 多个线程的工作内存彼此独立,互不可见,线程启动的时候,虚拟机为每个内存分配一块工作内存,不仅包含了线程内部定义 ...

  5. SQL语言DDL DML DCL TCL四种语言

    1.DDL(Data Definition Language)数据库定义语言:DDL使我们有能力创建或删 除表格.可以定义索引(键),规定表之间的链接,以及施加表间的 约束. • 常见DDL 语句: ...

  6. Java关键字volatile的实现原理(四)

    简述 volatile 是轻量级的synchronized,在多线程开发中保证了共享变量的可见性.可见性就是当一个线程修改一个共享变量时,另一个线程可以读到修改的值.如果volatile变量使用恰当, ...

  7. 双重检查锁单例模式为什么要用volatile关键字?

    前言 从Java内存模型出发,结合并发编程中的原子性.可见性.有序性三个角度分析volatile所起的作用,并从汇编角度大致说了volatile的原理,说明了该关键字的应用场景:在这补充一点,分析下v ...

  8. GoF23:单例模式(singleton)

    目录 单例模式简介 常见五种单例模式的实现方式 饿汉式 懒汉式 DCL懒汉式 饿汉式改进(静态内部类式) 枚举单例 防止反射破坏单例模式 单例模式简介 核心作用:保证一个类只有一个实例,并且提供一个访 ...

  9. Java中volatile关键字你真的理解了吗?

    面:你怎样理解volatile关键字时? 我:不加思索的说出,volatile修饰的成员变量,可保证线程可见性.不保证原子性和禁止指令重排. 面:你能谈谈什么是线程可见性吗? 我:各个线程对主内存中共 ...

随机推荐

  1. [线段树]Codeforces 339D Xenia and Bit Operations

    Xenia and Bit Operations time limit per test 2 seconds memory limit per test 256 megabytes input sta ...

  2. 发布内容需要的Markdown语法

    发布内容需要的Markdown语法 目录 发布内容需要的Markdown语法 [toc] 1.概述 1.1设计理念 1.2内联HTML语法 1.3特殊字符自动转义 2.行内语法讲解 2.1注释的表述 ...

  3. ASP.NET Core Authentication and Authorization

    最近把一个Asp .net core 2.0的项目迁移到Asp .net core 3.1,项目启动的时候直接报错: InvalidOperationException: Endpoint CoreA ...

  4. 使用TensorFlow v2库实现线性回归

    使用TensorFlow v2库实现线性回归 此示例使用简单方法来更好地理解训练过程背后的所有机制 from __future__ import absolute_import, division, ...

  5. Java——类的定义

    对象和类的关系:有一个学生 ,需要在表格上填写自己的信息 ,那么这个打印机就像一个类 ,打印出的表格就是一个对象,用类创建对象,学生填的信息 ,就是我所初始化的信息. 类的组成:由 属性(也叫成员变量 ...

  6. dyld

    一.介绍 在 MacOS 和 iOS 上,可执行程序的启动依赖于 xnu 内核进程运作和动态链接加载器 dyld. dyld 全称 the dynamic link editor,即动态链接器,其本质 ...

  7. 1068 Find More Coins (30分)(dp)

    Eva loves to collect coins from all over the universe, including some other planets like Mars. One d ...

  8. 1051 Pop Sequence (25分)

    Given a stack which can keep M numbers at most. Push N numbers in the order of 1, 2, 3, ..., N and p ...

  9. 1028 List Sorting (25 分)

    Excel can sort records according to any column. Now you are supposed to imitate this function. Input ...

  10. iOS, Xcode11,项目提示第三方库报错无法运行 bundle format unrecognized, invalid, or unsuitable

    检查你有没有把静态库和动态库配置错误!! 下图处是配置动态库的地方! 对于动态库和静态库都有使用的时候,注意把静态库设置成“Do not Embeded”