多线程-单例模式 - Double Check Lock - Volatile

2023-10-29

单例模式(Singleton):

保证一个类在程序中仅有一个实例,并提供一个访问该实例的全局访问点。

单例模式的实现-饿汉模式:

设计思想:在类加载时就创建一个不可变的静态的单例对象。(如果该对象不被使用,则浪费了堆空间的资源)

//问题1: 为什么加final
//答:可以防止子类通过操作生成父类的对象,不适当的覆盖父类的方法,从而破坏单例
//问题2:如果实现了序列化接口,还要做什么来防止反序列化破坏单例、
//答:通过在readResolve方法中,返回INSTANCE来防止反序列化破坏单例
public final class Singleton implements Serializable {
    //问题3:为什么设置构造方法为私有?是否能防止反射创建新的实例?
    //回答:1.防止通过new 构造器的方式,创建单例类的对象。设置为私有不能防止反射创建单例
    private Singleton(){
    }
    //问题4:这样初始化是否能保证单例对象创建时的线程安全?
    //回答:可以的,线程是安全的,静态变量的初始化操作是在类加载中完成的。
    private static final Singleton INSTANCE = new Singleton();
    //问题5:为什么提供静态方法而不是将INSTANCE设置为public,说出你知道的理由
    //回答:1.提供更好的封装性,2.也便于修改为懒汉模式 3.也可以提供泛型的支持,4.还能再获取单例对象前做一些自定义的逻辑
    public static Singleton getInstance(){
        return INSTANCE;
    }

    public Object readResolve(){
        return INSTANCE;
    }
}

关于懒汉模式的解析:

问题1:为什么要用final关键字修饰单例类

答:这样做可以防止子类继承单例类后,不适当的覆盖了父类的方法,最后导致破坏了单例

问题2:如果单例类实现了Serializable 序列化接口,如何防止反序列化破坏单例

答:通过在readResolve方法中,返回INSTANCE来防止反序列化来破坏单例

问题:3:为什么要将单例类的构造方法私有化?将构造方法私有化能否防止反射创建新的实例

答:将单例类的构造方法私有化是为了防止通过new 构造器的方式在外部创建单例类的对象。将单例类的构造方法私有化并不能防止反射创建新的实例

问题4:通过 static final 创建静态的不可变变量是否能单例对象创建时的线程安全。

答:可以的,静态变量的初始化操作是在类加载中完成的。

问题5:为什么提供静态方法而不是将INTANCE设置为public

答:1.提供更好的封装性;2.便于扩展修改为懒汉模式;3.也可以提供泛型的支持;4.便于在单例对象创建是做一些其他的自定义操作

 

单例模式-懒汉模式:

设计思想,在类加载时先不创建单例对象,等到需要使用的时候创建单例对象(节省资源)。

实现1(存在线程安全问题-并不能保证单例对象只有一个实例):

/**
 * @author jiangl
 * @version 1.0
 * @date 2021/4/13 9:43
 */
public class MySingleton {

    private static MySingleton instance= null;

    private MySingleton() {
    }


    public final static MySingleton getInstance(){
        if(instance == null){
            instance = new MySingleton();
        }
        return instance;
    }
}

分析:

当程序中没有MySingleton对象实例,多线程同时调用getInstance1()方法时,可能会创建多个实例。

 

实现2(线程安全,也保证了程序中只有一个单例对象实例,但是性能较差):

/**
 * @author jiangl
 * @version 1.0
 * @date 2021/4/13 9:43
 */
public class MySingleton {

    private static MySingleton instance= null;

    private MySingleton() {
    }


    public final static MySingleton getInstance(){
        synchronized (MySingleton.class){
            if(instance == null){
                instance = new MySingleton();
            }
        }
        return instance;
    }
}

分析:

使用该方式创建的单例对象,第一次使用单例,多线程同时访问时,只有一个线程能获取到锁,其他线程进入阻塞。等到获取到锁的线程创建完了instance对象,其他线程进去同步代码块时判断instance是否为null。

此时的instance 已经被创建了,所以其他线程可以直接返回instance的单例对象实例。

但是该方法存在问题,第一次使用单例对象后的每次调用所有的线程都会去尝试获取锁,并且进入同步代码块做判断,非常消耗性能。

 

实现3:双检锁 double check locking(dcl) 性能上优化,在第一次使用后,后续的调用不需要去获取锁。

存在的问题,由于存在指令重排序,会导致线程获取到了“不为空,但是未调用构造函数的对象”。

/**
 * @author jiangl
 * @version 1.0
 * @date 2021/4/13 9:43
 */
public class MySingleton {

    private static MySingleton instance= null;

    private MySingleton() {
    }


    /**
     * double checked locking (dcl) 存在问题
     * @return
     */
    public final static MySingleton getInstance2(){
        //先判断是否非空
        if(instance == null){
            //加锁,只有获取到锁的线程才能创建
            synchronized (MySingleton.class){
                //第一创建的时候,多个线程竞争锁,未获取到锁的线程进入阻塞,当其他线程
                if(instance ==null){
                    instance = new MySingleton();
                }
            }
        }
        return instance;
    }

}

分析:

该方法在第一次使用单例对象时,多线程同时访问,竞争获取锁,创建对象。后续访问则不获取尝试获取锁,从而提高性能

 

问题:

由于synchronized并不能保证内部代码的指令重排序,所以可能导致

反编译后的字节码

//获取instance变量
0 getstatic #2 <MySingleton.instance>

//判断instance变量是否不为空,如果为空则跳转到37 行 返回对象
3 ifnonnull 37 (+34)

6 ldc #3 <MySingleton>
8 dup
9 astore_0

//获取同步监视器,进入同步代码块
10 monitorenter
11 getstatic #2 <MySingleton.instance>
14 ifnonnull 27 (+13)

//instance为空 创建对象
17 new #3 <MySingleton>

//复制引用
20 dup

//调用构造方法创建对象
21 invokespecial #4 <MySingleton.<init>>

//将引用复制给静态变量
24 putstatic #2 <MySingleton.instance>
27 aload_0
28 monitorexit
29 goto 37 (+8)
32 astore_1
33 aload_0
34 monitorexit
35 aload_1
36 athrow
37 getstatic #2 <MySingleton.instance>
40 areturn

其中:

17表示创建对象,将对象引用入栈//new Singleton

20表示复制一份对象引用

21表示利用一个对象引用,调用构造方法

24表示利用一个引用,赋值给static INSTANCE

也许jvm会优化为:先执行24将引用复制给staic INSTANCE,在执行21调用对象引用的构造方法,

此时如果两个线程来访问,t1线程先调用了24:putstatic将instance赋值了,此时t2线程访问判断instance不为空,则正常返回了instance 的正常使用,但这个instance的实例还没有调用构造方法

导致t2线程使用异常

 

实现4:对静态变量加上 volatile修饰,防止指令重排序

/**
 * @author jiangl
 * @version 1.0
 * @date 2021/4/13 9:43
 */
public class MySingleton {

    /**
     * 加入volatile 防止指令重排序
     */
    private volatile static MySingleton instance= null;

    private MySingleton() {
    }

    /**
     * double checked locking (dcl)
     * @return
     */
    public final static MySingleton getInstance(){
        //先判断是否非空
        if(instance != null){
            return instance;
        }
        //加锁,只有获取到锁的线程才能创建
        synchronized (MySingleton.class){
            //第一创建的时候,多个线程竞争锁,未获取到锁的线程进入阻塞,当其他线程
            if(instance ==null){
                instance = new MySingleton();
            }
            return instance;
        }

    }


}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程-单例模式 - Double Check Lock - Volatile 的相关文章

  • 在 Spring Boot 中重新加载/刷新缓存

    我正在使用 Spring Boot 对于缓存 我使用 Ehcache 到目前为止一切正常 但现在我必须重新加载 刷新 那么我该如何执行此操作 以便我的应用程序不会出现任何停机时间 我在Spring Ehcache中尝试了很多方法 但它不起作
  • 如何检查 Java 中的间隔列表 (Joda-Time) 是否完全覆盖一个月

    我在用着乔达时间 http www joda org joda time Java 中用于跟踪时间列表的库间隔 http www joda org joda time key interval html 我想检查是否有一个列表Interva
  • Java简单加密

    我想加密存储在磁盘上的文本 配置 文件 尝试使用DES http en wikipedia org wiki Data Encryption Standard加密 我在客户端计算机上遇到了致命错误 后来我发现该算法无法处理重音字符 我怀疑这
  • TestNG 启动期间发生内部错误

    我创建了一个 TestNG 类 FirstTest java 当我将测试用例作为 TestNG Test 运行时 出现以下错误 期间发生内部错误 启动 FirstTest java lang NullPointerException Ecl
  • 如何在 Android 中恢复我的音频?

    我必须实现用于创建具有暂停和恢复状态的音频的应用程序 当我的应用程序作为启动时音频启动 当我按下模拟器上的后退按钮时 音频音乐处于暂停状态 但是当我的活动回来时从停止状态到前台我的音频音乐未恢复 这是我的代码 public class Au
  • SimpleDateFormat 无法正确处理 DD

    我正在尝试获得这样的格式 2013 06 15 17 45 我在代码中执行以下操作 Date d new Date SimpleDateFormat ft new SimpleDateFormat YYYY MM DD HH mm Stri
  • java模拟自定义对象

    public class MainClass public void makeCall CustomObject obj new CustomObject obj testMethod 我想进行单元测试makeCall 所以我必须嘲笑Cus
  • 使用 Thymeleaf 时我们应该删除 HTML 属性吗?

    我正在研究 Thymeleaf 发现几乎所有示例中都有 Thymeleaf 的标签值以及标准 HTML 值 例如 这些
  • 从另一个类添加 Swing 组件

    我正在学习java 我正在尝试从另一个类向我的框架添加一个菜单栏 练习将代码划分为多个类以更好地组织程序 这是我的代码示例 public class MainApp public static void main String args C
  • 为什么 Cassandra 客户端在生产中没有 epoll 时会失败? [复制]

    这个问题在这里已经有答案了 当我在本地运行服务时 我收到一条警告 指出 epoll 不可用 因此它使用 NIO 很公平 当我将其部署到 Kubernetes 中时 我得到了以下信息 这导致服务无法运行 2017 03 29T19 09 22
  • APACHE POI 从 Java 中的 Excel 获取精确的字体颜色

    在 Excel 工作表中 如何使用 Java 中的 Apache POI 获取准确的字体颜色值 我试图通过使用来获取字体颜色 org apache poi ss usermodel Font f book getFontAt style g
  • 使用泛型进行选择排序

    我对整数进行了选择排序并且它正在工作 当我尝试修改程序以使用泛型时 编译器会抱怨 我不知道如何修复它 如果有人能提出一些建议和建设性意见 我将不胜感激 这是代码 public class SelelctionSort public stat
  • 使用 Lint 和 SonarQube 分析 Android 项目

    我真的 溢出 了试图让这些东西一起工作 我按照这里的指示进行操作 http docs sonarqube org display PLUG Android Lint Plugin http docs sonarqube org displa
  • 从字符串中提取文本 Java

    使用此字符串 ADACADABRA 如何从java中的字符串 ADACADABRA 中提取 CADA 以及如何提取 和 之间的id从下面的链接 http www youtube nocookie com embed zaaU9lJ34c5
  • JSF“总”变量类似于 JSTL 中的 c:set

    我不喜欢 JSF 但我需要用它来解决这个问题 我正在 纯 JSF 中工作 所以这就是我基本上需要的 但我不知道如何用 JSF 来实现它
  • 飞碟 - html 实体未呈现

    我正在使用 Flying saucer lib 生成 pdf 但我对一些 html 实体有问题 我已经在寻找解决方案 我在这个论坛和其他地方找到了很多提示 但仍然存在问题 我尝试过这种方法 http sdtidbits blogspot c
  • 为什么我的 Java 路径中添加了“L”?

    我在我的类路径中加载了一个 jar 在 iReport 中 如果重要的话 我确信它具有所需的方法 但是当我尝试测试连接 从而调用该 jar 时 我得到一个 java lang NoSuchMethodError 说它正在引用班上 Lorg
  • 从 IntelliJ 运行 JavaFX 应用程序

    Versions openjdk版本 11 0 11 2021 04 20 OpenJDK 运行时环境 build 11 0 11 9 Ubuntu 0ubuntu2 20 10 OpenJDK 64 位服务器虚拟机 内部版本 11 0 1
  • 使用 Spring Batch 将文件中的日期解析为 LocalDateTime

    我正在尝试使用 Spring Batch 读取包含日期的 CSV 文件 但在将日期解析为LocalDateTime Object 字段 日期 上的对象 目标 中的字段错误 拒绝值 2017 07 20 04 15 25 0 代码 typeM
  • selenium 没有找到合适的方法,直到(ExpectedCondition)

    这是有线的问题 我导入的项目运行 100 几个月前 今天我已将其与依赖项一起导入 但存在问题WebDriverWait 这是我的代码 WebDriverWait driverWait new WebDriverWait driver 100

随机推荐

  • (安装pclpy)pclpy+windows+anaconda

    pclpy 0 12 0版本 支持windows的python3 6和3 7版本 但pclpy 0 12 0移除了可视化模块 即看不到点云效果了 安装方法 进入对应python版本的虚拟环境 然后输入 pip install pclpy 0
  • kylin在hadoop 中的架构图_kylin跨集群配置实现读写分离

    社区提供的读写分离架构图如下 通过架构图可以看到Kylin会访问两个集群的HDFS 建议两个集群的NameService务必不能相同 尤其是集群启用NameNode HA时 相同的NameService会导致组件在跨集群访问HDFS时因无法
  • nginx配置ssl证书https解决公网ip可以访问但是域名不行的问题

    进入nginx文件夹 将下载得到的crt和key文件放到这个目录下 以下来自腾讯云官方 https cloud tencent com document product 400 35244 server SSL 访问端口号为 443 lis
  • 警告:[SetPropertiesRule]Setting property 'source' to xxx did not find a matching property.的消除

    启动JSP页面时报错 全文如下 九月 25 2016 7 47 39 下午 org apache tomcat util digester SetPropertiesRule begin 警告 SetPropertiesRule Serve
  • Python(练习七)

    一 max 0 count 0 while True num int input Enter a number 0 for end of input if num 0 break if num gt max max num count 1
  • mongodb如何使用授权登录

    前言 mongodb默认是不需要授权登录的 这样在实际生产环境中是非常危险的一件事情 接下来就来讲一下如何开启安全授权访问 1 第一次登录不启动授权 默认就是不启动 我们先来创建admin和root账号 他们是用来开启授权后操作用户 创建数
  • PWNHUB 一场新鲜赛事速达【六月内部赛】 web - login game + Misc - 伏羲八卦

    PWNHUB 一场新鲜赛事速达 六月内部赛 web login game Misc 伏羲八卦 web login game Misc 伏羲八卦 本文来自csdn的 shu天 平时会记录ctf 取证和渗透相关的文章 欢迎大家来我的主页 shu
  • thinkpad笔记本如何进bios设置u盘启动步骤

    thinkpad笔记本从u盘启动有两种方法 一种是使用u盘启动快捷键直接进入u盘装系统 另一种则需要进bios设置u盘为第一启动项 但首先要下载个u盘启动盘制作工具制作成启动u盘在进行 下面详细为大家介绍如何操作 方法一 使用u盘启动快捷键
  • 脚本一:编写一个脚本要求检测文件类型(简化版)

    要求 1 命名为check file sh 2 检测判断它是否存在 3 判断它是否为普通文件 4 判断其是否为目录 5 判断其是否为软链接 6 如没有文件名则报错 编写脚本如下 验证文件如下 可见如果文件不存在 直接报错 如果符合条件直接给
  • 《Web安全基础》05. XSS · CSRF · SSRF · RCE

    web 1 XSS 1 1 简介 1 2 防护与绕过 1 2 1 HttpOnly 1 2 2 WAF 绕过 1 3 相关资源 2 CSRF 3 SSRF 4 RCE 本系列侧重方法论 各工具只是实现目标的载体 命令与工具只做简单介绍 其使
  • 行人属性识别的一个调研

    行人属性识别的一个调研 知乎 前言 我感觉我掌握了财富密码 从知乎的后台数据来看 大家貌似更喜欢看综述多一点 因此这次给大家整个 行人属性识别 PAR 的综述 同样的 这次的综述比较老 是19年的 大家酌情看 适合入门用 首先还是保命时刻
  • 【会议分享】2022年智能车国际会议(ICoIV 2022)

    2022年智能车国际会议 ICoIV 2022 重要信息 会议网址 www icoiv org 会议时间 2022年10月14 16日 召开地点 中国成都 截稿时间 2022年8月30日 录用通知 投稿后2周内 收录检索 EI Scopus
  • 爬虫从入门到精通(8)

    文章目录 一 多进程和多线程介绍 二 普通爬虫 三 多线程爬虫 1 普通方法调用 2 线程类调用 四 多进程爬虫 1 普通方法调用 2 进程类写法 五 gevent协程爬虫 1 gevent模块简介 2 安装和依赖 3 gevent协程爬虫
  • 【深度学习】AlexNet

    从AlexNet开始 一 不可否认 深度学习的热潮正是由2012年AlexNet的出现而引发的 因此 学习AlexNet网络的结构 对于CNN的学习与理解是不可或缺的 在本篇博客中 将会对AlexNet的论文进行翻译与解读 并在下一篇博客中
  • android 壁纸服务,Android-Service实现手机壁纸自动更换

    本文实例为大家分享了Android Service实现自动更换手机壁纸的具体代码 供大家参考 具体内容如下 先看下效果 使用界面 划重点 使用service前别忘了给相应的service添加服务 具体实现 首先定义ChangeService
  • Dialog的exec和open

    今天在使用窗口时 发现了这样一个问题 抽象代码如下 结果第一次调用窗口时 一切正常 但是第二次调用时 窗口里的内容全部消失了 只有一个空白窗口 解决方法 调用open 函数 exec 函数会将程序卡到那里 open 函数生成窗口后立即返回
  • 祭旗之作

    渺小的我 一如原来那样的懒散 我拼命的使自己跟的牛人学习 但是 我总是掉队 我不愿意平庸 但是对于任何事情感觉都是没有坚持到最后 应该更加的坚持 犹豫与多虑 是我的性格 我渴望一直坚持下去 但好多时候 由于时间的关系 选择了放弃 我依然是我
  • SGL基本思路讲解

    SGL图形库是为Windows图形界面编程服务的 而且一切都是考虑到对新手友好的 在具体介绍提供给用户的函数之前 需要先说明一下应该以什么样的思路来构思我们的SGL程序 作为C语言程序 main函数总是需要在最开始就了解一下的 在SGL库中
  • STM32的中断与事件

    这张图是一条外部中断线或外部事件线的示意图 图中信号线上划有一条斜线 旁边标志19字样的注释 表示这样的线路共有19套 图中的蓝色虚线箭头 标出了外部中断信号的传输路径 首先外部信号从编号1的芯片管脚进入 经过编号2的边沿检测电路 通过编号
  • 多线程-单例模式 - Double Check Lock - Volatile

    单例模式 Singleton 保证一个类在程序中仅有一个实例 并提供一个访问该实例的全局访问点 单例模式的实现 饿汉模式 设计思想 在类加载时就创建一个不可变的静态的单例对象 如果该对象不被使用 则浪费了堆空间的资源 问题1 为什么加fin