<多线程章节八> 单例模式中的饿汉模式与懒汉模式的讲解,以及懒汉模式中容易引起的Bug

news2024/11/18 15:45:28

💐专栏导读

本篇文章收录于多线程,也欢迎翻阅博主的其他文章,可能也会让你有不一样的收获😄
🌷JavaSE 🍂多线程 🌾数据结构

文章目录

  • 💐专栏导读
  • 💡饿汉模式
  • 💡懒汉模式
  • 💡懒汉模式多线程版
  • 💡volatile防止指令重排序

单例模式是一种经典的设计模式了,它的作用就是保证在有些场景下, 需要一个类只能有一个对象,而不能有多个对象,比如像你以后娶媳妇,你娶媳妇肯定是只能娶一个,而不能娶两个;

但是,问题来了,一个类只需要一个对象,那在new对象的时候只new一次对象不就可以了么,为什么还要弄个这么麻烦的东西呢?

因为啊,只new一次对象确实是只有一个,但是呢,如果你在写代码的过程中忘了呢,然后又new了一次,这种概率是很大的,毕竟,人是最不靠谱的动物😅,就像是有一句话说的好:宁可相信世界上有鬼,也不要相信男人的那张嘴😂,所以的,为了防止这种失误发生,就有了单例模式,在Java中也有许多类似的机制,比如final,就会保证修饰的变量肯定是不能改变的;@override,保证你这方法肯定是一个重写方法;**interface,**保证肯定要重写接口里面的方法;这些都是在语法方面进行了一些限制,但是,在语法方面,对于单例并没有特定的语法,所以,这里就通过编程技巧来达到类似的限制效果;

单例模式的两种实现方式:

💡饿汉模式

1.在类中实例化类的对象,给外界提供一个方法来使用这个对象;

2.将构造方法用private修饰,保证在类外不能再实例化这个类对象

public class SingleTon {

    //在类的内部实例化对象
    public static SingleTon instance = new SingleTon();
    //定义一个方法,用来获取这个对象
    //后序如果类外的代码想要使用对象时,直接调用这个方法即可
    public static SingleTon getInstance() {
        return instance;
    }
    //设置一个私有的构造方法,保证在这个类外无法实例化这个对象
    private SingleTon(){

    }
}

在这里插入图片描述

可以看到,这里的对象被static修饰,所以在类被加载的时候创建,创建的时机就比较早,并且被static修饰的对象只会被创建一次,所以这种在类加载时就创建实例的模式称为饿汉模式

💡懒汉模式

懒汉模式单线程版:

这样的写法与上面的相同点就是:同样在类外不能再第二次实例化对象,不同点是:将创建对象的时机放在getInstance方法中,这样在类加载的时候就不会创造实例,而是当第一次调用这个方法时才会去创建

public class SingleTon {
    public static SingleTon instance = null;
    //定义一个方法,用来获取这个对象
    //后序如果类外的代码想要使用对象时,直接调用这个方法即可
    public static SingleTon getInstance() {
        //懒汉模式
        if(instance == null) {
            instance = new SingleTon();
        }
        return instance;
    }
    //设置一个私有的构造方法,保证在这个类外无法实例化这个对象
    private SingleTon(){
        
    }
}

在这里插入图片描述

💡懒汉模式多线程版

在线程安全方面,上面的饿汉模式是线程安全的,而懒汉模式在多线程下是不安全的;

因为,如果多个线程同时访问一个变量,那么不会出现不安全问题,如果多个线程同时修改一个变量,就有可能出现不安全问题;

饿汉模式下,只进行了访问,没有涉及到修改

在这里插入图片描述

懒汉模式下,不仅进行了访问,还涉及了修改,那么下面就讲解以下懒汉模式在多线程下如何会产生不安全

在这里插入图片描述
在这里插入图片描述

既然出现了不安全问题,那么如何将懒汉模式修改成安全的呢?

💡方法:进行加锁,使线程安全

在这里插入图片描述

但是,如果锁加在这个地方,仍然是不安全的,因为,这样还是会进行穿插执行,并没有保证它是一个整体(f非原子性)

在这里插入图片描述

并不是加了锁就安全,只有锁加对了才会安全,在加锁的时候要保证以下几方面:

1.锁的 {} 的范围是合理的,能够把需要作为整体的每个部分都包括进去;

2.锁的对象能够起到锁竞争的效果;

懒汉模式多线程版改进👇

将if语句和new都放在锁里面称为一个整体,这样就避免了会穿插执行;

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

但是上述代码还有一个问题,每当调用getInstance时,都会尝试去进行加锁,而加锁是一个开销很大的操作,而这里的懒汉模式之所以会出现线程不安全问题,是因为只是在在第一次调用getInstance方法进行new对象时,可能会出现问题,但是,只要new完对象以后,就不用再进行锁竞争再去进行if判断了,直接访问就可以了,所以再次进行优化👇:

    public static SingleTon getInstance() {
        //在最外面在进行一次判断
        if(instance == null) {
            synchronized (SingleTon.class) {

                if(instance == null) {
                    instance = new SingleTon();
                }

            }
        }
        return instance;
    }

在第一次实例化对象后,以后再调用个getInstance方法时,就不会再创建对象,而且也不会再去获取锁,因为,第一个if判断语句都不会进去,所以不会执行到加锁的语句;

上面的单例模式看着好像是完全没问题了,但是,还是有一个问题,就是可能会触发指令重排序问题,所以就需要使用volatile解决指令重排序问题

💡volatile防止指令重排序

指令重排序:编译器会保证在你代码逻辑不变的情况下,对代码进行优化,使代码的性能得到提高,这样的操作称为指令重排序;

举个例子:

在这里插入图片描述
在这里插入图片描述

在代码中,在实例化对象这一步可能会出现指令重排序问题,下面就来讲解一下为什么👇

在这里插入图片描述

在这里插入图片描述

对于上述的指令重排序问题,解决方案就是:使用volatile关键字修饰singleTon

**线程安全的单例模式(懒汉模式)**👇

public class SingleTon {
    //使用volatile关键字修饰,防止指令重排序
    public static volatile SingleTon singleTon = null;
    public static SingleTon getSingleTon() {
        if(singleTon == null) {
            synchronized (SingleTon.class) {
                if(singleTon == null) {
                    singleTon = new SingleTon();
                }
            }
        }
        return singleTon;
    }
    private SingleTon() {

    };

}

💡💡这里再次提醒,使用单例模式要注意三个要点:

  • 加锁
  • 两层if判断
  • 使用volatile修饰引用,防止指令重排序

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

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

相关文章

GCC、g++、gcc的关系

GCC、g、gcc的关系 引言 VsCode中对编译环境进行配置的时选择编译器时发现有多种不同的编译器 GNU计划和GCC GNU的全称 GNU’s Not UNIX GNU是一个计划 Q:为什么会有这个计划 因为当时的Unix开始收费和商业闭源,有人觉得不爽→ 想要自己开发和Unix类似的→GNU计划 GUN计划目…

uni-app/vue 文字转语音朗读(附小程序语音识别和朗读)uniapp小程序使用文字转语音播报类似支付宝收款播报小程序语音识别和朗读)

uni-app/vue 文字转语音朗读(小程序语音识别和朗读) uniapp小程序功能集合 1、uniapp小程序文字转语音播报 一、第一种方式:直接加语音包 固定的文本 先利用工具生成了 文本语音mp3文件,放入项目中,直接用就好了 …

Linux下自动挂载U盘或者USB移动硬盘

最近在折腾用树莓派(实际上是平替香橙派orangepi zero3)搭建共享文件服务器,有一个问题很重要,如何在系统启动时自动挂载USB移动硬盘。 1 使用/etc/fstab 最开始尝试了用/etc/fstab文件下增加:"/dev/sda1 /home/orangepi/s…

CS224W1.3——图表示的选择

文章目录 1. 图网络构成2. 选择一个合适的表示3. 图结构实例3.1 二部图3.2 图的表示 4. 节点和边的属性 这小节主要讲图表示的选择。 1. 图网络构成 对于每个实体,我们创建节点 N N N,对于每个关系,我们创建边 E E E,对于整体而言…

PTE-写作 学习(一)

目录 PTE写作 写作技能 词汇积累 熟悉机经 pte写作考的就是态度 写作技能 看一段写一句 蓝色框里的单词是不可以使用的 ,他们是副词,要添加新的句子 PTE写作 写作技能 词汇积累 熟悉机经 题库太窄 pte写作考的就是态度 写作技能 极有模板可…

Linux下protobuf和 protobuf-c安装使用

如果在 C语言中使用 protobuf,就需要使用 protobuf-c这个库。 protobuf使用详解:https://blog.csdn.net/qq_42402854/article/details/134066566 下面在 Linux下安装 protobuf和 protobuf-c。 一、下载 protobuf和 protobuf-c 官方的 Protocol Buffer提…

绿野仙踪不仅是童话,还是便宜又好用的产品测试法!

以 ChatGPT 为代表的大语言模型爆火后,推动了对话类人工智能产品的高速发展,我们已经看到了如智能助理、问答系统、自动写作等多种类型的个性化对话类 AI 服务。 AI 能力的提升让人们对智能 AI 产品的期望越来越高,相关产品的用户体验也因此变…

供应 JOSEF约瑟 跳位合位监视继电器 JZ-7GJ-S002XMC AC220V

品牌:JOSEF约瑟名称:跳位、合位、电源监视继电器型号:JZ-7GJ-S002XMC额定电压:AC220V功率消耗:≤1.1W触点容量:110V0.5A 系列型号: JZ-7GY-S002XMT跳位、合位、电源监视继电器; JZ-…

27 行为型模式-解释器模式

1 解释器模式介绍 //用于两个整数相加的方法 public static int add(int a , int b){ return a b; } //用于三个整数相加的方法 public static int add(int a , int b,int c){ return a b c; } public static int add(Integer ... arr){ int sum 0; for(Integer num : arr)…

降级熔断:如何屏蔽非核心系统故障的影响?

目录 前言 一、熔断是什么? 二、服务降级 三、雪崩是如何发生的 四、hystrix使用 五、降级机制要如何做 总结 前言 在“双十一”的巨大流量中,商品促销过程中出现了几次短暂的服务不可用,这给部分用户造成了不好的使用体验。事后&…

编译运行windows+OpenMVG+OpenMVS+vs2017

安装vcpkg过程需要翻墙!!! github下载代码 git clone https://github.com/microsoft/vcpkg git clone https://github.com/cdcseacave/VCG.git git clone https://github.com/cdcseacave/openMVS.git src安装vcpkg包 cd .\vcpkg .\bootstr…

电脑桌面可以设置半透明皮肤的待办事项软件推荐哪一款

无论你是在家工作还是在办公室,无论你是学生还是职场人士,每天都有许多任务等待着你。在这些琐事中,很容易遗漏一些重要的工作。那么,如何才能高效地记录和管理这些任务呢?生活和工作中的琐碎任务常常让我们感到忙碌而…

Hadoop、Hive安装

一、 工具 Linux系统:Centos,版本7.0及以上 JDK:jdk1.8 Hadoop:3.1.3 Hive:3.1.2 虚拟机:VMware mysql:5.7.11 工具下载地址: https://pan.baidu.com/s/1JYtUVf2aYl5–i7xO6LOAQ 提取码: xavd…

关于云主机root无法从VNC登录处理

一、问题描述 某次基线加固过程中,一线反馈离开工位后,返回时原root登录会话断开,使用普通用户无法切到root,尝试使用移动云控制台进行VNC登录,但也提示登录失败:报:incorrect auth 二、处理…

Nautilus Chain 联合香港数码港举办 BIG DEMO DAY活动,释放何信号?

在今年的 10 月 26 日 9:30-18:30 GMT8 期间,Nautilus Chain 联合香港数码港共同举办了 “BIG DEMO DAY” Web3 项目路演活动,包括Xwinner、Sleek、Tx、All weather、Coral Finance、DBOE、PARSIQ、Hookfi、Parallels、Fintestra 以及 dot.GAMING 等在内…

1763. 最长的美好子字符串

1763. 最长的美好子字符串 java代码&#xff1a; class Solution {public String longestNiceSubstring(String s) {int n s.length();int maxPos 0;int maxLen 0;for (int i 0; i < n; i) {int lower 0;int upper 0;for (int j i; j < n; j) {if (Character.isL…

(四)库存超卖案例实战——优化redis分布式锁

前言 在上一节内容中&#xff0c;我们已经实现了使用redis分布式锁解决商品“超卖”的问题&#xff0c;本节内容是对redis分布式锁的优化。在上一节的redis分布式锁中&#xff0c;我们的锁有俩个可以优化的问题。第一&#xff0c;锁需要实现可重入&#xff0c;同一个线程不用重…

Linux 应用程序CPU调度优化

缘起 实时操作系统&#xff08;Real-time operating system, RTOS&#xff09;&#xff0c;又称即时操作系统&#xff0c;它会按照排序运行、管理系统资源&#xff0c;并为开发应用程序提供一致的基础。实时操作系统与一般的操作系统相比&#xff0c;最大的特色就是实时性&…

如何通过员工工时管理降低企业成本?

作为当今快节奏商业环境的领导者或管理者&#xff0c;掌握员工的工作时间对于控制企业成本和确保每个人都各尽其责至关重要。 员工工时表软件就是这样一款工时跟踪管理解决方案&#xff1a;数字化的工时表有助于保护企业的财务不会被无节制的开支冲垮。然而&#xff0c;引入此…

windows PC virtualBox 配置

效果&#xff1a; oracle vitualbox 可以访问通PC主机&#xff0c;可以访问外网: 注意&#xff0c;如果docker0网络地址&#xff0c;和PC主机的网络地址冲突了&#xff0c;需要变更docker的网络地址&#xff1a; root/home/mysqlPcap/anti-tamper $ cat /etc/docker/daemon.js…