指令重排与更安全地单例模式创建,加深理解

Android Volatile 关键字学习 面试官:你平时是怎么创建单例的? 我:我一般用DCL双重检锁的方式来创建单例,然后为 instance 加上 volatile 修饰,防止 DCL 失效。 面试官:那你可以具体说说 volatile 吗? 我:行! 前言 相信很多 Andorid程序员跟我一样,最开始接触到 volatile 这个关键字是在创建单例的时候,如: public class SingleTon { //为了防止出现 DCL失效问题,加上 volatile 关键字 private static volatile. 阅读详情

多线程下『指令重排』会影响正确性,例如著名的 double-checkedlocking 模式实现单例

   public static Singleton getInstance( )  {
       // 实例没创建,才会进入内部的 synchronized代码块
        if(INSTANCE == null) {
            synchronized (Singleton.class) {
                // 也许有其它线程已经创建实例,所以再判断一次
                if(INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }

线程1来了,发现对象没创建,进入同步代码块,锁住类对象,执行下面的代码,如果同一时间,第二线程也来操作,就会等待第一个线程执行结束,第一线程同步代码块锁解开了,第二线程进入了,如果第二个线程进来以后,如果没有内层判断,就会又创建一个对象,等第二个线程进来以后还要加一个if判断,如果为空了再创建,创建好了就直接返回instance就好了。

以上的实现特点是:

  • 懒惰实例化

  • 首次使用 getInstance() 才使用 synchronized 加锁,后续使用时无需加锁

但在多线程环境下,上面的代码是有问题的,没有考虑指令重排的问题, 重点代码在:INSTANCE = new Singleton() 对应的字节码为:

0: new #2 // class cn/itcast/jvm/t4/Singleton 执行结果会把对象引用放入操作数栈
3: dup   //操作数栈把对象复制了一份。第一个交给了invokespecial调用构造方法,第二个交给了putstatic
4: invokespecial #3 // Method "<init>":()V
7: putstatic #4 // Field
INSTANCE:Lcn/itcast/jvm/t4/Singleton;

其中 4 7 两步的顺序不是固定的,也许 jvm 会优化为:先将引用地址赋值给 INSTANCE 变量后,再执行构造方法,如果两个线程 t1,t2 按如下时间序列执行:

时间1 t1 线程执行到 INSTANCE = new Singleton();
时间2 t1 线程分配空间,为Singleton对象生成了引用地址(0 处)
时间3 t1 线程将引用地址赋值给 INSTANCE,这时 INSTANCE != null(7 处)
时间4 t2 线程进入getInstance() 方法,发现 INSTANCE != null(synchronized块外),直接
返回 INSTANCE
时间5 t1 线程执行Singleton的构造方法(4 处)

这时 t1 还未完全将构造方法执行完毕,如果在构造方法中要执行很多初始化操作,那么 t2 拿到的是将是一个未初始化完毕的单例

对 INSTANCE 使用 volatile 修饰即可,可以禁用指令重排,但要注意在 JDK 5 以上的版本的 volatile 才会真正有效。

单例懒惰创建
 
//禁用指令重排
private static volatile Singleton INSTANCE = null;
​
public static Singleton getInstance( )  {
        if(INSTANCE == null) {
            synchronized (Singleton.class) {
                if(INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
DOORS需求管理实战:从汽车设计案例看模块化需求拆分技巧 本文通过一个汽车设计实战案例,深入解析了如何利用DOORS进行模块化需求拆分与管理。从捕获原始用户需求到精炼为可追踪的系统需求,再到按子系统(如自动驾驶)进行模块化拆分,文章详细展示了构建需求骨架、建立可追溯性以及实施变更控制的完整流程,帮助团队在复杂系统工程中实现从混沌到秩序的高效协同。 阅读详情

相关推荐

别再到处找模板了!手把手教你用Vue3+Element Plus搭建一个‘能跑起来’的管理后台原型

本文详细介绍了如何利用Vue3和Element Plus在72小时内快速搭建一个功能完整的管理后台原型。从环境准备到项目获取、依赖安装、原型定制、数据模拟以及构建优化,手把手教你打造一个可直接演示的后台系统,特别适合创业者快速验证产品逻辑。

weixin_26854555的博客 182

指令重排导致的单例在并发场景下出现问题!

欢迎关注公众号(文章末尾即可扫码关注) ,持续在我后台回复 「资料」 可领取在我后台回复「面试」可领取该文章内容已经收录在,包含底层原理解析,带你冲破面试迷雾。

qq_45260619的博客 1352

保姆级教程:用ROS2 Humble和Nav2从零搭建一个能自主导航的机器人小车(含SLAM建图与避障)

本文提供了一份详细的保姆级教程,指导如何使用ROS2 Humble和Nav2从零搭建一个具备自主导航功能的机器人小车,包括SLAM建图与避障。教程涵盖了硬件组装、ROS2环境配置、URDF模型创建、SLAM建图实战、Nav2导航栈配置以及系统集成调试等关键步骤,适合机器人开发初学者和进阶开发者。

weixin_30329623的博客 487

JVM之内存模型

详解JVM内存模型,详解JMM中的原子性、可见性、有序性,详解 CAS 与 原子类、synchronized 的优化

真滴不可理喻的博客 677

2024年五大革新图像分割模型解析:架构创新与实战应用指南

本文深入解析2024年五大革新图像分割模型,包括SAM2、Mask2Former、DINOv2和Swin Transformer等,探讨其架构创新与实战应用。从多尺度处理、提示交互到自监督学习,这些模型在精度提升、效率优化和交互增强方面取得突破,广泛应用于医疗影像、工业质检和智慧城市等领域。

hadoop5ranger的博客 894

ANSYS WB 网格划分详解与技巧

本文详细介绍了网格操作中的关键设置与功能选项。主要内容包括:1)网格显示风格与物理偏好设置;2)单元阶次选择对精度的影响;3)单元尺寸控制方法;4)尺寸调整功能,重点讲解了自适应尺寸调整、增长率控制和捕获曲率等选项;5)网格质量评估标准;6)四种主要划分方法(四面体、六面体主导、扫掠、多区域)的特点与适用场景;7)三种尺寸调整方式(直接调整、边缘尺寸调整、影响范围调整)的具体应用。文章强调在实际建模中需要根据模型特点合理选择网格参数,平衡计算精度与效率。

2403_87939837的博客 1694

初始JavaEE篇——多线程(4):wait、notify,饿汉模式,懒汉模式,指令重排序

wait、notify方法,多线程练习,单例模式:饿汉模式、懒汉模式,指令重排序

我要学编程的博客 2302

光伏发电系统模拟及其发电预测开源python工具pvlib

本文介绍光伏发电系统模拟及其发电预测开源python工具pvlib,以及举例介绍获取辐照度方法、光伏发电预测思路。

肖永威的专栏 1万+

【026期】面试官:说说双重检查加锁单例模式为什么两次校验?

其它面试题(springboot、mybatis、并发、java中高级面试总结等)《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取![外链图片转存中…(img-vgSmoAiQ-1713388640324)]《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2401_83329402的博客 1155

免费大容量国外网盘测评:冷数据归档与rclone加密接入

云存储的本质,是通过对象存储、冗余编码和分层缓存,把本地磁盘容量问题转化为网络访问与数据管理问题。免费大容量国外网盘常以空间换用户规模或付费转化,容量政策、传输策略、协议支持和退出成本,往往比标称容量更决定长期可用性。支持 WebDAV、S3 兼容和命令行工具的服务,可接入 rclone 等自动化流程,再配合客户端加密与冷热分层,用于冷数据归档、老项目备份和家庭影像保存。理解带宽换算、单文件上限、保活机制与传输一致性后,评估话题型大容量网盘与专业向协议网盘,就能把免费空间纳入可靠存储层。围绕免费大容量国外网

weixin_31292729的博客 210

用 OP-TEE 给 AI 模型“上锁”:密文存储、TEE 解密放行、推理后销毁(实战可落地)

本文提出一种基于OP-TEE的AI模型保护方案,通过两级密钥体系确保模型静态存储为密文,运行时仅在TEE内解密。方案采用KEK+DEK架构,DEK永不出TEE,支持分块解密与可验证擦除。该方案能有效防御静态分析、应用层逆向等威胁,但需承认其物理级攻击的局限性。文章详细描述了端到端架构、文件格式设计及TA实现要点,包括密钥管理、分块解密策略和销毁机制,为设备侧AI部署提供了工程可行的安全解决方案。

Interview_TC的博客 288

加深Java对象的底层知识理解

背景 对象的创建过程? DCL与volatile问题? 对象在内存中的存储布局? 对象头具体包括什么? 对象怎么定位? 对象怎么分配? Object o = new Object(),在内存中占用多少字节? 过程 对象的创建过程 小结 ...

sanding的博客 198

第二篇:APS智能排产实战:OR-Tools与coptpy从入门到案例

专注于具体的技术实现,包括 OR-Tools 和 coptpy 的详细使用指南、建模技巧、日志解读,以及一个完整的两阶段生产排程案例。适合准备动手开发 APS 系统的工程师和研究者。

算法工程师 452

volatile使用和Java单例模式中的volatile理解

为了更好理解单例模式中的volatile之前,需要先简单了解下Java的内存模型。 Java的内存模型 Java内存模型简称JMM(Java Memory Model),是Java虚拟机所定义的一种抽象规范,用来屏蔽不同硬件和操作系统的内存访问差异,让java程序在各种平台下都能达到一致的内存访问效果。 Java内存模型是什么样子的呢?可以理解为下图的样子: 解释下: 1、主内存(Main M...

魔教中人 435

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【多线程3】java内存模型以及指令重排序理解

背景 掌握好多线程知识对java内存模型的了解也是必不可少的还有指令重排序问题。 java内存模型抽象结构 线程1将x=1写入本地内存,然后本地内存在刷入主内存; 线程2将x=1从主内存中读取出来,然后在存入本地内存。 指令重排序 重排序是值编译器和处理器为了优化程序性能而对指令序列进行冲洗排序的一种手段。 我们先看下面的代码 package com.enjoy.cap1; /** * @author: dalele * @date: 2020/11/21 19:53 * @description:

大乐乐 175

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Java并发11:Java内存模型、指令重排、内存屏障、happens-before原则

[超级链接:Java并发学习系列-绪论] 本章主要对Java并发中非常重要的概念Java内存模型、指令重排和happens-before原则进行学习。 1.内存模型 如果想要设计表现良好的并发程序,理解Java内存模型是非常重要的。 Java线程之间的通信由Java内存模型(Java Memory Model,简称JMM)控制。 JMM决定一个线程对共享变量的写入何时对另一个线...

韩超的博客 (hanchao5272) 5600

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

一文带你彻底搞懂设计模式之单例模式!!由浅入深,图文并茂,超超超详细的单例模式讲解!!

本篇文章通过图文形式,由浅入深,详细讲解了设计模式中的单例模式的应用

祝你身体健康,美满幸福 1万+

java中final与volatile-线程安全问题

在线程安全问题中final主要体现在安全发布问题上,在这里先讲一下什么事安全发布,在《java并发编程实践》一书中有讲,不过看起来挺难懂的…. public class Holder { private int n; public Holder(int n) { this.n = n; } public void assertSanity() ...

weixin_41858951的博客 460

DCL为什么要用volatile关键字?

先看一下DCL(双重检查锁模式)的示例代码: public class Singleton { //Singleton对象属性,加上volatile关键字是为了防止指定重排序,要知道singleton = new Singleton()拆分成cpu指令的话,有足足3个步骤 private volatile static Singleton singleton; //对外...

硅脂味咖啡的博客 3529

掉了两根头发,可算是把volatile整明白了

为什么只能保证可见性?又是怎么实现禁用指令重排?哇,原来这么简单

成都彭于晏 2万+

一起走进单例模式

一.介绍 单例模式(Singleton Pattern)属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象(不需要手动去new)。本文会结合源码加深你对单例模式的理解 二.实现方式 大体上分为四种实现方式:饿汉式、懒汉式、静态内部类、枚举类 实现思想基本一致: 提供一个唯一的私有实例 私有化构造器 提供一个公共方法获取私有实例 饿汉式 /*

a347635191的博客 673

认识多线程:单例模式

单例模式的两种实现(饿汉模式简单安全,懒汉模式需双重检查+volatile保证线程安全)阻塞队列的特性及生产者消费者模型(解耦+削峰填谷的关键工具)阻塞队列的手动实现(基于锁和条件变量的经典并发编程实践)掌握这些基础知识,是深入学习多线程编程的基础。实际开发中,JDK提供了丰富的并发工具类(如等),理解其底层原理能帮助我们更好地运用它们解决实际问题。

nice_lcj520的博客 424

Java面试内容精讲—并发编程volatile

前言 观看链接:https://www.bilibili.com/video/BV1RC4y1h7YU。 课程总体说明 介绍的是有关于并发编程下的volatile关键字的使用; 并发编程一直是java实际开发当中一个非常重要的内容,同时也是一个难以理解的内容; 在实际开发中或者是在面试中经常被面试官问到关于并发编程的知识点;volatile就是其中一个比较热门的知识点,也是相对于来说比较难以理解的知识点; 在介绍volatile关键字之前,先来了解一下并发编程的背景; 并发编程是在互联网的背景下,当我们的系

qq_43409111的博客 234

单例模式

单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式有两种实现方式懒汉式和饿汉式,前者可以一般可以由编程语言本身保证多线程环境下不会有并发问题,而后者需要程序员自己加锁实现并发访问。java中其实推荐使用enum来实现单例。

changer328的专栏 673

JUC从实战到源码:再识volatile

本章将深入探讨volatile的特性,包括其如何实现内存可见性和禁止指令重排,以及内存屏障在其中的作用。

qq_43843951的博客 1027

Java面试题:使用单例模式优化线程安全的缓存管理,使用装饰者模式优化对象的并发访问,利用生产者-消费者模式解决并发数据处理

通过上述三道面试题的探讨,我们不仅复习了Java设计模式、内存管理和并发编程的关键知识点,还通过实际的编程问题加深了对这些概念的理解。掌握这些核心技术,将有助于我们在面对复杂编程挑战时,能够更加从容不迫,构建出更加健壮和高效的Java应用程序。

码农超哥的博客 535
上一篇: 字节码拿捏i++和++i
下一篇: 一篇掌握Jep灵活应用,数学公式自定义
happystudy_neu
博客等级 码龄4年 1140粉丝 43原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值