DCL与延迟初始化(单例)

news2024/11/19 15:21:27

前言

在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销

第一种(存在问题)

public class UnsafeLazyInitialization {
    private static Instance instance;
    public static Instance getInstance() {
        if (instance == null)               // 1:A线程执行
            instance = new Instance();      // 2:B线程执行
        return instance;
    }
}

假设A线程执行代码1的同时,B线程执行代码2。此时,线程A可能会看到instance引用的对象还没有完成初始化。(对象初始化可能发生重排序,单线程无影响,多线程可能出现问题)

创建对象

image-20230715090441732

创建对象三部曲(正确顺序):
1、分配对象的内存空间
2、初始化对象
3、设置 instance 指向刚分配的内存地址

步骤2、3可能重排序,重排序后:

image-20230715090559133

此时,这个重排序在没有改变单线程程序执行结果的前提下,可以提高程序的执行性能(因此可能发生)。

然而多线程情况下,就会出现上述问题。

第二种(改进第一种)

public class SafeLazyInitialization {
    private static Instance instance;
    public synchronized static Instance getInstance() {
        if (instance == null)
            instance = new Instance();
        return instance;
    }
}

方法上加个synchronized,使线程串行执行,不会出现问题,但是synchronized锁的代码块太多,锁颗粒太大,导致性能不是特别好。

第三种(DCL)

public class DoubleCheckedLocking {

    // volatile 保证创建对象时不会发生重排序
    private volatile static Singleton instance;

    public static Singleton getInstance() {

        // 第一次检查 (代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化。)
        if (instance == null) {
            // synchronized锁更少的代码性能会好一些
            synchronized (DoubleCheckedLocking.class) {  // 加锁
                if (instance == null)                    // 第二次检查
                    /*
                        问题的根源出在这里,对象不加 volatile 修饰,创建对象时可能发生重排序,导致先创建一个空对象,后续在初始化,单线程最后结果不会受到影响所以可以重排序,此时另一个线程第一次检查不为null,直接返回结果但是此时对象还未初始化解决问题只需将 对象由 volatile修饰禁止指令重排即可
                     */
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

image-20230715091847930

与方法一类似,由于创建对象可能发生重排序,导致多线程情况下,线程可能拿到未初始化的对象。

两个办法来实现线程安全的延迟初始化:

  1. 不允许2和3重排序。
  2. 允许2和3重排序,但不允许其他线程“看到”这个重排序。

为了保证创建对象不会发生重排序,单例对象应该使用volatile修饰。(基于volatile的解决方案)

第四种(基于类初始化的解决方案)

public class InstanceFactory {
    private static class InstanceHolder {
        public static Instance instance = new Instance();
    }
    public static Instance getInstance() {
        // 这里将导致InstanceHolder类被初始化
        return InstanceHolder.instance ;  
    }
}

JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁(初始化锁:与类或者接口一一对应)。这个锁可以同步多个线程对同一个类的初始化(保证类只会被初始化一次)。JVM在类初始化期间会获取这个初始化锁,并且每个线程至少获取一次锁来确保这个类已经被初始化过了

对于类或接口的初始化,Java语言规范制定了精巧而复杂的类初始化处理过程。(自行探索)

volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/756057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础如何自学成为网络安全工程师

前言 一份网络攻防渗透测试的学习路线,不藏私了! 👉 【一帮助安全学习一】👈这里自取256G网络安全自学资料 1、学习编程语言(phpmysqljshtml) 原因: phpmysql可以帮助你快速的理解B/S架构是怎样运行的&#xff0c…

【AI底层逻辑】——篇章5(上):机器学习算法之回归分类

目录 引入 一、何为机器学习 1、定规则和学规则 2、算法的定义 二、机器学习算法 1、常见学习方法 2、回归 3、分类 续下篇... 往期精彩: 引入 计算机发明初,专家通过将专业知识和经验梳理成规则输入计算机程序,但是这样跟不上知识…

IT技术培训班:搭乘学习快车的抉择

引言: 在IT技术学习的道路上,我们常常会被推荐各种五花八门的技术培训班。它们通过各种宣传手段向我们展示着美好的未来和无限的机会。然而,我们又应该如何看待这些培训班呢?在培训班里学技术真的有用吗?本文将从不同角…

【Java进阶之路】HashMap源码分析(JDK1.8)

概述 JDK 1.8 对 HashMap 进行了比较大的优化,底层实现由之前的 “数组链表” 改为 “数组链表红黑树”,本文就 HashMap 的几个常用的重要方法和 JDK 1.8 之前的死循环问题展开学习讨论。 JDK 1.8 的 HashMap 的数据结构如下图所示,当链表节…

Docker 替代方案:适用于 SaaS 应用程序的 10 种 Docker 替代方案

Docker技术已经在基础设施管理领域引起了革命性的变化,以至于Docker现在已经成为容器的代名词。重要的是要理解,所有的Docker都是容器,但并非所有的容器都是Docker。虽然Docker是最常用的容器技术,但还有其他几种替代Docker的选择…

积分兑换小程序项目总结

1. 项目概述 背景:中标项目,第三方公司做会员福利,以积分的形式发放。目标:给固定的钱,积分兑完,周期两个月。需求:固定会员能及时线上兑换积分。解决方案:开发微信小程序在线兑换。…

Nexpose v6.6.203 for Linux Windows - 漏洞扫描

Nexpose v6.6.203 for Linux & Windows - 漏洞扫描 Rapid7 Vulnerability Management, Release Jul 05, 2023 请访问原文链接:https://sysin.org/blog/nexpose-6/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.o…

【吴恩达】prompt engineering(原则 迭代 文本概括 推断、订餐机器人)

简介 Introduction 基础的LLM训练的模型,问法国的首都什么,可能会将答案预测为“法国最大的城市是什么,法国的人口是多少”许多 LLMs 的研究和实践的动力正在指令调整的 LLMs 上。指令调整的 LLMs 已经被训练来遵循指令。因此,如…

(EMQX)STM32L+BC20+MQTT协议传输温湿度,ADC,电压,GPS数据到EMQX

1、材料准备 准备以下材料 2、设备连接 2.1 插入物联网卡,天线 首先把BC20核心板从开发板上拆下来 然后将物联卡放置在BC20核心板内 物联卡放置完成将BC20核心板重新插入到开发板内(注意不要弄错方向) 同时接入天线 2.2 连接ST-Link仿真…

Android VNDK/VSDK Snapshot编译框架

1.背景 背景一: 为解决Android版本碎片化问题,引入Treble架构,它提供了稳定的新SoC供应商接口,引入HAL 接口定义语言(HIDL/Stable AIDL,技术栈依然是Binder),它指定了 vendor HAL 和system fram…

使用GPU进行大规模并行仿真,解决强化学习采样瓶颈:CPU、GPU架构以及原理详解

强化学习的落地应用场景,我认为可以是仿真环境仿真程度高,且仿真速度快的任务场景。而这篇帖子将会将:使用 GPU 进行大规模并行仿真,解决强化学习采样瓶颈。并直接举出三个例子,展示如何对原有的仿真环境进行修改,让它们适应 GPU 并行加速。 1.强化学习论文背后的仿真环…

用 GPU 并行环境 Isaac Gym + 强化学习库 ElegantRL:训练机器人Ant,3小时6000分,最高12000分

前排提醒,目前我们能 “用 ppo 四分钟训练 ant 到 6000 分”,比本文的 3 小时快了很多很多,有空会更新代码 https://blog.csdn.net/sinat_39620217/article/details/131724602 介绍了 Isaac Gym 库 如何使用 GPU 做大规模并行仿真,对环境模块提速。这篇帖子,我们使用 1 …

JMeter websocket接口测试

前言 在一个网站中,很多数据需要即时更新,比如期货交易类的用户资产。在以前,这种功能的实现一般使用http轮询,即客户端用定时任务每隔一段时间向服务器发送查询请求来获取最新值。这种方式的弊端显而易见: 有可能造…

docker仓库,搭建registry仓库

什么是仓库 docker仓库是用来包含镜像的位置,docker提供一个注册服务器registry来保存多个仓库,每个仓库又可以包含多个具备不同的镜像。docker运行中使用的默认仓库是docker hub公共仓库 docker hubdocker push username/busybox:latest docker hub是docker公司维护的公共仓…

java项目之电影院售票网站(ssm+mysql+jsp)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的电影院售票网站。技术交流和部署相关看文章末尾! 开发环境: 后端: 开发语言:Java 框架&a…

pytest 禁用警告信息(忽略警告信息输出)

如图示例代码,提示test_001这个case 存在警告 新增pytest.ini 配置文件 [pytest] filterwarnings errorignore::UserWarning

MIT 6.S081 Lab 11 -- NetWork -- 上

MIT 6.S081 Lab 11 -- NetWork -- 上 引言Network背景你的工作(hard)提示 Lab 解析E1000 网卡重点内容官方手册摘录3.2 Packet Reception3.3 Packet Transmission 小结 引言 本文为 MIT 6.S081 2020 操作系统 实验十一解析。 MIT 6.S081课程前置基础参考: 基于RISC-V搭建操作…

CPU之IPC相关

什么是IPC? IPCInstructionUnhaltedCyclethread Instruction,即某个固定时间段内系统完成的指令数,考虑到系统中任何形式的应用都是由指令完成,且在不修改应用(代码逻辑)的前提下完成固定的任务所需的指令…

关于godot游戏引擎中常见的小数除以大数结果为0的问题

这种问题常见于求百分比的需求。 如:5/100 , 6/120 ,前面的数小,后面的数字大 这种情况下,可以使用5.0/100 来进行解决,或者 5.0/100.0 或者6.0 / 120 或者 6.0/120.0 关于这个细节,余数为0…

基于SpringBoot+vue的心灵治愈交流平台设计与实现

博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…