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

💐专栏导读

本篇文章收录于多线程,也欢迎翻阅博主的其他文章,可能也会让你有不一样的收获😄
🌷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修饰引用,防止指令重排序

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java spring boot 注解、接口和问题解决方法(持续更新)

注解 RestController 是SpringMVC框架中的一个注解,它结合了Controller和ResponseBody两个注解的功能,用于标记一个类或者方法,表示该类或方法用于处理HTTP请求,并将响应的结果直接返回给客户端,而不需要进行视图渲染…

数据分享|R语言逻辑回归、线性判别分析LDA、GAM、MARS、KNN、QDA、决策树、随机森林、SVM分类葡萄酒交叉验证ROC...

全文链接:http://tecdat.cn/?p27384 在本文中,数据包含有关葡萄牙“Vinho Verde”葡萄酒的信息(点击文末“阅读原文”获取完整代码数据)。 介绍 该数据集(查看文末了解数据获取方式)有1599个观测值和12个变量&#xf…

华锐技术何志东:证券核心交易系统分布式改造将迎来规模化落地阶段

近年来,数字化转型成为证券业发展的下一战略高地,根据 2021 年证券业协会专项调查结果显示,71% 的券商将数字化转型列为公司战略任务。 在落地数字化转型战略过程中,证券业核心交易系统面临着不少挑战。构建新一代分布式核心交易…

长沙某公司面经总结 - 失败版

1.Java语言的特征 Java的三大特性:封装、继承、多态 面向对象是利于语言对现实事物进行抽象。面向对象具有以下特征: 继承:继承是从已有类得到继承信息创建新类的过程 封装:封装是把数据和操作数据的方法绑定起来,对…

快速入门:使用 Spring Boot 构建 Web 应用程序

前言 本文将讨论以下主题: 安装 Java JDK、Gradle 或 Maven 和 Eclipse 或 IntelliJ IDEA创建一个新的 Spring Boot 项目运行 Spring Boot 应用程序编写一个简单的 Web 应用程序打包应用程序以用于生产环境 通过这些主题,您将能够开始使用 Spring Boo…

Fourier分析导论——第2章——Fourier级数的基本属性(E.M. Stein R. Shakarchi)

第 2 章 Fourier级数的基本属性(Basic Properties of Fourier Series) Nearly fifty years had passed without any progress on the question of analytic representation of an arbitrary function, when an assertion of Fourier threw new light on the subject. Thus…

前端环境的安装 Node npm yarn

一 node npm 1.下载NodeJS安装包 下载地址:Download | Node.js 2.开始安装 打开安装包后,一直Next即可。当然,建议还是修改一下安装位置,NodeJS默认安装位置为 C:\Program Files 3.验证是否安装成功 打开DOS命令界面&#…

基于jsp,ssm物流快递管理系统

开发工具:eclipse,jdk1.8 服务器:tomcat7.0 数据库:mysql5.7 技术: springspringMVCmybaitsEasyUI 项目包括用户前台和管理后台两部分,功能介绍如下: 一、用户(前台)功能: 用…

无线振动传感器安装

lora无线温振一体传感器即传感器的采集时间,采集方式完全有主机通过命令实现。其主要特点是:传感器平时处在低功耗状态、传感器可以随时响应远程主机控制命令、传感器可采集特征值或者原始加速度数据 lora 技术,提高了传输速率多振动&#xf…

程序开发官网地址汇总

这里写目录标题 官网地址汇总开发环境开发工具数据库驱动包其他 官网地址汇总 开发环境 1 JDK :https://www.oracle.com/java/technologies/java-se-glance.html 2 Maven:https://maven.apache.org/download.cgi 3 Maven Repository: https://mvnrep…

项目管理-挣值管理例题-使用SV进度偏差和CV成本偏差来判断进度和成本是否合适

基础概念介绍 CV和SV的计算公式 在财务分析中,常常会用到CV和SV这两个指标。CV是成本偏差,SV是进度偏差。它们的计算公式如下: CV EV - AC SV EV - PV 其中,EV是挣值,AC是实际成本,PV是计划价值。 …

Qt之基于QCustomPlot绘制直方图(Histogram),叠加正态分布曲线

一.效果 二.原理 1.正态分布 高斯分布(Gaussian distribution),又名正态分布(Normal distribution),也称"常态分布",也就是说,在正常的状态下,一般的事物,都会符合这样的分布规律。 比如人的身高为一个随机变量,特别高的人比较少,特别矮的也很少,大部分都…

idea插件(一)-- SequenceDiagram(UML自动生成工具)

目录 1. 安装 2. 默认快捷键 3. 操作说明 4. 导出为图片与UML类图 4.1 导出为图片: 4.2 导出 UML 类图 SequenceDiagram是从java、kotlin、scala(Beta)和groovy(limited)代码生成简单序列图(UML&…

【Java】多线程案例(单例模式,阻塞队列,定时器,线程池)

❤️ Author: 老九 ☕️ 个人博客:老九的CSDN博客 🙏 个人名言:不可控之事 乐观面对 😍 系列专栏: 文章目录 实现安全版本的单例模式饿汉模式类和对象的概念类对象类的静态成员与实例成员 懒汉模式如何保证…

微信小程序面试题之理论篇

本文内容,来源于极客学院的分享,这里只做引用。 说说你对微信小程序的理解?优缺点? 背景 小程序与H5 优缺点 优点:缺点: 说说微信小程序的生命周期函数有哪些? 应用的生命周期页面的生命期组件的生命周期执行过程 应…

【前端早早聊直播回顾】Harmony Next 与 Flutter 的不解之缘

Hello 大家好,我是 Flutter GDE 郭树煜,本次要分享的话题是关于鸿蒙与 Flutter 的故事,可能没接触过的会感觉有点懵,Harmony 和 Flutter 有啥关系,它们怎么会被放到一起讲了呢?接下来就让我们来聊聊这个问题…

Apache Dolphinscheduler如何不重启解决Master服务死循环

个人建议 Apache Dolphinscheduler作为一个开源的调度平台,目前已经更新到了3.X版本,4.0版本也已经呼之欲出。3.0版本作为尝鲜版本,新添加了许多的功能,同时也存在非常多的隐患,本人使用3.0版本作为生产调度也踩了很多…

【C++】C++11常见特性

🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风…

Selenium3-获取元素的css属性

来个元素CSS值 代码1: 通过javaScript执行脚本获取css值 String jsStr "return document.getElementById(\"buyers\").style.getPropertyValue(width)"; Object o ((JavascriptExecutor) driver).executeScript(jsStr); System.out.println(o.toString(…

机器学习-特征工程

一、特征工程介绍 1.1 什么是特征 数值特征(连续特征)、文本特征(离散特征) 1.2 特征的种类 1.3 特征工程 特征是机器学习可疑直接使用的,模型和特征之间是一个循环过程; 实际上特征工程就是将原始数据…