普通单例模式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. openshift 平台上部署 gitlab代码仓库服务

    背景: 本文档将以在openshift 平台上部署 gitlab 服务来验证集群各个服务组件的可用性以及熟悉openshift的使用方法.服务部署方式可以多种多样,灵活部署.本篇以常见的镜像部署方式来 ...

  2. 力扣 - 768. 最多能完成排序的块II

    目录 题目 思路 代码实现 复杂度分析 题目 这个问题和"最多能完成排序的块"相似,但给定数组中的元素可以重复,输入数组最大长度为2000,其中的元素最大为10**8. arr是一 ...

  3. 《为研发同学定制的MySQL面试指南》-- 连载中

    Hi大家好,我是来自博客园的赐我白日梦! 为大家带来MySQL面试专题!全文110篇!以问答的方式,由浅入深的帮你应对各类MySQL面试题的狂轰滥炸!当然也不乏会分享一些高阶读写分离数据库中间件原理及 ...

  4. php ci下添加一个创建常用的模块和控制器方法

    我这么写是非常不好的 ,这些都可以写在lirbraries里面 (ci就是这么干的) 我这里是自己用 大概一个模型 没那么多讲究 现在core/CodeIgniter.php 文件 if($modle ...

  5. c#视频位置

    static void Main(string[] args)        { string scoure = @"C:\Documents and Settings\Administra ...

  6. SpringBoot入门最简单的一个项目示例

    使用IDEA创建一个SpringBoot项目 1.1 打开IDEA,文件-New-Project 1.2下一步,选择版本8(根据自己安装的JDK版本来选择) 1.3 下一步后点击Web,勾选Sprin ...

  7. 深入学习OpenCV文档扫描及OCR识别(文档扫描,图像矫正,透视变换,OCR识别)

    如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 下面 ...

  8. 利用MultipartFile来进行文件上传

    这个例子实在SpringMVC的基础上完成的,因此在web.xml中需要配置 web.xml <!-- 配置Spring MVC的入口 DispatcherServlet,把所有的请求都提交到该 ...

  9. 压缩法备份Linux系统文件

    在使用Ubuntu之前,相信很多人都有过使用Windows系统的经历.如果你备份过Windows系统,那么你一定记忆犹新:首先需要找到一个备份工 具(通常都是私有软件),然后重启电脑进入备份工具提供的 ...

  10. NIO源码分析:SelectionKey

    SelectionKey SelectionKey,选择键,在每次通道注册到选择器上时都会创建一个SelectionKey储存在该选择器上,该SelectionKey保存了注册的通道.注册的选择器.通 ...