普通单例模式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()这句代码,编译后将会拆分出多个指令(以下只取三个关键指令描述)

  1. 初始化对象成员变量并赋默认值
  2. 将为我们要赋值给成员变量的值替换掉默认值
  3. 将对象的地址赋值给对应的引用

由于CPU存在指令重排的优化机制,在多线程访问时可能会影响到我们的执行结果,如下将2,3指令掉个顺序

  1. 初始化对象成员变量并赋默认值
  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的更多相关文章

  1. 对volatile的理解--从JMM以及单例模式剖析

    请谈谈你对volatile的理解 1.volitale是Java虚拟机提供的一种轻量级的同步机制 三大特性1.1保证可见性 1.2不保证原子性 1.3禁止指令重排 首先保证可见性 1.1 可见性 概念 ...

  2. 【并发编程】Volatile原理和使用场景解析

    目录 一个简单列子 Java内存模型 缓存不一致问题 并发编程中的"三性" 使用volatile来解决共享变量可见性 volatile和指令重排(有序性) volatile和原子性 ...

  3. Volatile禁止指令重排序(三)

    Volatile禁止指令重排 计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令重排,一般分为以下三种: 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系 ...

  4. 单例模式+volatile禁止指令重排序

    单例模式: 单例,顾名思义就是只能有一个.不能再出现第二个.就如同地球上没有两片一模一样的树叶一样. 在这里就是说:一个类只能有一个实例,并且整个项目系统都能访问该实例. 单例模式共分为两大类: 懒汉 ...

  5. volatile原理和应用场景

    volatile是java语言中的一个关键字,常用于并发编程,有两个重要的特点:具有可见性,java虚拟机实现会为其满足Happens before原则;不具备原子性.用法是修饰变量,如:volati ...

  6. 单例模式的两种实现方式对比:DCL (double check idiom)双重检查 和 lazy initialization holder class(静态内部类)

    首先这两种方式都是延迟初始化机制,就是当要用到的时候再去初始化. 但是Effective Java书中说过:除非绝对必要,否则就不要这么做. 1. DCL (double checked lockin ...

  7. 由单例模式学到:lock锁

    lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁. lock (xxx) { // Critical code section. } lock 关键字可确保当一 ...

  8. double check 解决单例模式的多线程并发问题

      最近被多线程问题(multi-thread issue)弄昏了头.以前虽然也知道系统里要考虑多线程问题,也无数次见到double-check的代码,但是由于自己碰到这方面的问题基本上就是从其他地方 ...

  9. Java程序员面试必备:Volatile全方位解析

    前言 volatile是Java程序员必备的基础,也是面试官非常喜欢问的一个话题,本文跟大家一起开启vlatile学习之旅,如果有不正确的地方,也麻烦大家指出哈,一起相互学习~ 1.volatile的 ...

随机推荐

  1. 湖湘杯2020_ReMe

    查壳后发现是由Python2.7环境下编译得到的exe可执行文件 由此想到可将exe转为pyc文件再反编译成py文件 且该方法只适用于py2 无混淆 因为py3的字节码结构有些许变化 step1: 在 ...

  2. python给图片打马赛克

    图片马赛克 关注公众号"轻松学编程"了解更多. 导包 import numpy as np import matplotlib.pyplot as plt %matplotlib ...

  3. insert into select 和select into from 备份表

    一 insert into select要求表必须存在 INSERTINTO order_record SELECT * FROM order_today FORCEINDEX (idx_pay_su ...

  4. 【Kata Daily 190920】Square(n) Sum(平方加总)

    题目: Complete the square sum function so that it squares each number passed into it and then sums the ...

  5. UGC

    文章目录    4 UGC推荐        4.1 基于用户UGC标签进行推荐            4.1.1 最简单的算法(SimpleTagBased)            4.1.2 利用 ...

  6. webpack配置babel篇

    babel-polyfill & babel-runtime & babel-preset-env babel-core babel-core 的作用是把 js 代码分析成 ast , ...

  7. CentOS 8.x 下尝试安装.Net 5 的运行时

    1.背景 看着不管是群里还是公众号里这几天最热闹就是.Net 5.0 正式版的发布.C#9. 当然要开发.net 5.0 的项目就需要把VisualStudio升级的v16.8.0版本了.升级后自带着 ...

  8. 92. Reverse Linked List II 翻转链表II

    Reverse a linked list from position m to n. Do it in one-pass. Note: 1 ≤ m ≤ n ≤ length of list. Exa ...

  9. 创建Grafana监控视图

    前言 Grafana允许查询,可视化,警报和了解指标,无论它们存储在哪里. 可视化:具有多种选项的快速灵活的客户端图.面板插件提供了许多不同的方式来可视化指标和日志. 动态仪表盘:使用模板变量创建动态 ...

  10. 使用Ganglia监控系统监控集群(debian)

    ganglia是一个集群监控软件,底层使用RRDTool获得数据. Ganglia分为ganglia-monitor和gmetad两部分,前者运行在集群每个节点上(被监控机器)收集RRDTool产生的 ...