javaEE初阶——多线程(四)

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享多线程专题的第四篇(关于多线程代码案例中的单例模式)
如果有不足的或者错误的请您指出!

目录

  • 九、多线程代码案例(单例模式)
    • 1.单例模式
      • 1.1饿汉模式
      • 1.2懒汉模式
      • 1.3使用场景
      • 1.4上述单例模式的线程安全问题
      • 1.5指令重排序问题

九、多线程代码案例(单例模式)

1.单例模式

单例模式是设计模式里面比较简单的一种,顾名思义,单例就是只有一个实例,也就是对于进程中的某个类,它只能实例化一个对象,不会new出来多的对象
那我们如何保证一个类只能有一个对象呢???单凭我们口头保证肯定是不行的,我们就需要通过特定代码的手段来实现这个功能
单例模式我们主要认识两种写法:饿汉模式、懒汉模式

1.1饿汉模式

表示这个类的唯一实例创建得比较早,类似于饿了很久的人,一看到吃的就迫不及待想去吃
用代码体现就是:

public class Singleton {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}

static修饰的其实是类属性,就是在"类对象"上的,每个类的类对象在jvm中只有一个,那么里面的静态成员就只有一个了
此处后续需要使用这个类的实例,就可以直接通过getInstance来获取已经new好的这个,而不是重新new
但是我们怎么保证,这个类就只能实例化一次呢??我们直接将构造方法私有化即可

public class Singleton {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
    private Singleton(){}//代码里面不需要有任何逻辑
}

将构造方法私有化是单例模式里面最核心的一步,类之外的代码尝试实例化对象的时候,就必须要通过构造方法,但是由于构造方法是私有的,无法调用,尝试实例化的时候就会编译出错
我们验证一下:
在这里插入图片描述
当我们尝试实例化对象:
在这里插入图片描述
就会编译出错

可能有人会问两种极端情况
(1)如果我在一个Singleton类里面实例化多个对象,那不就打破了单例模式了嘛??
实际上,如果你是这个类的作者,你一定不会去这么做;如果你是别人,使用这个类的时候,如果要进行修改,一般也要经过你的审核
(2)那么我们可以通过反射机制拿到私有的构造方法嘛??
原则上是可以的,但是在实际开发中,反射机制并不常见,甚至不能乱用,特殊场景下回需要用到反射,但是使用反射要付出很大的代价(会严重影响代码的可读性和封装性)

1.2懒汉模式

在计算机领域,"懒"往往是提高效率的表现
在懒汉模式中,不是在程序启动的时候就实例化好唯一对象了,而是在后续使用这个类的时候才去创建实例,此时如果不去使用这个类,那么创建实例的代价就很好地省下了

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

    private SingletonLazy(){}
    
}

什么时候使用这个类就什么时候创建这个实例,本质上就是能偷懒的,能少做的就少做

1.3使用场景

代码中的有些对象,本身就不适合是有多个实例的,从业务角度就应该是单个实例

比如,你写的服务器,要从硬盘上加载100G的数据到内存里面,肯定要写一个类,封装上述加载操作,并且写一些获取/处理业务数据的业务逻辑

这样的类就应该是单例的,一个实例就管理100G的数据,搞N个实例就是N*100G的内存数据,机器肯定吃不消,也没必要,因为都是重复的数据

例如在java的JDBC中,DataSourse就应该是单例的,这种对象就类似于配置管理的对象(存储服务器的地址,端口号,用户名,密码,选项参数…)

1.4上述单例模式的线程安全问题

我们需要考虑,如果有多个线程同时调用getInstance(),线程是否安全??
如果是在饿汉模式下,实例创建的时间是程序启动的时候,比main线程调用还早,因而后续使用这个类的时候,调用getInstance()一定比创建实例要晚,此时就只是读取上述变量的值了,而在多个线程里同时读取同一个变量,是不会有线程安全问题的
我们主要是来看懒汉模式下的线程安全问题
在这里插入图片描述
此时一旦出现这种执行顺序,那么就会创建多一个实例
因此我们需要做的就是将if操作和new操作打包成一个原子操作
那么就要进行加锁操作

public class SingletonLazy {
    private static SingletonLazy instance = null;
    private static Object locker = new Object();
    public static SingletonLazy getInstance(){
        synchronized (locker) {
            if(instance == null){
                instance = new SingletonLazy();
            }
        }
        return instance;
    }

    private SingletonLazy(){}

}

但是还有一个细节问题,我们知道在懒汉模式下,只有在第一次调用getInstance()才是创建实例的.而一旦创建好了,后面的就只是读操作了
但是如果我们单纯这样加,那么后面尽管是读,也是需要加锁的,而加锁的开销是很大的,也有可能导致线程堵塞
因此我们可以在外层在加上if条件判断,如果需要创建实例,才加锁

public class SingletonLazy {
    private static SingletonLazy instance = null;
    private static Object locker = new Object();
    public static SingletonLazy getInstance(){
        if (instance == null) {
            synchronized (locker) {
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

    private SingletonLazy(){}

}

此时两个if的作用是不一样的,外层的if是为了防止没必要的加锁操作,里层的if是判断要不要加锁(如果两个线程都进去了第一层if,那么第二层if就是防止多创建对象)

同时,为了防止编译器进行优化操作,我们还需要对instance加上volatile关键字

public class SingletonLazy {
    private static volatile SingletonLazy instance = null;
    private static Object locker = new Object();
    public static SingletonLazy getInstance(){
        if (instance == null) {
            synchronized (locker) {
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

    private SingletonLazy(){}

}

1.5指令重排序问题

指令重排序实际上也是编译器优化的一种策略
我们写的代码在被编译成一条一条的二进制指令后,正常来说CPU都是按照顺序一条一条执行,但是编译器比较智能,会根据实际情况,调整指令执行的顺序,调整的目的就是为了提高效率
在单线程下,编译器的优化策略是比较安全的,能够保证优化前后代码的逻辑不变,但是在多线程环境下,就可能出现问题
就拿我们上面的单例模式 - 懒汉模式来说

instance = new SingletonLazy();

这一句代码,我们简单来看就可以分成3个步骤
(1)申请内存空间
(2)调用构造方法
(3)把此时内存的地址赋值到instance
在指令重排序下,可能会出现1,2,3或者1,3,2两种执行顺序(但是1一定是再前面的),在单例模式下这两种都是OK的
但是在多线程就可能会出现下面的情况:
在这里插入图片描述
在t1线程进行完地址赋值操作后,意味着instance就是非null,只是此时执行的对象是一个未初始化的对象
此时t2线程恰好进来,由于instance已经非空,第一个if的条件无法满足,就直接return了,如果在这个时候,进行Singleton s = …,s.func(),这里的后续操作都是针对未初始化的对象进行操作的,会出现严重的问题

解决上述问题的方法还是使用volatile
即volatile不仅能够解决内存可见性的线程安全问题,还能解决指令重排序的问题

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

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

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

相关文章

MacOS下Qt 5开发环境安装与配置

最近笔者在MacOS中使用Qt Creator开发Qt程序时遇到了一些问题,在网上查了不少资料,都没有找到解决方案,只有自己进行研究摸索了,今天晚上终于将目前遇到的问题全部解决了,特记录下来分享给大家。 笔者使用的是MacOS 1…

基于小程序实现的医院预约挂号系统

作者主页:Java码库 主营内容:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】:Java 【框架】:spring…

C++ | Leetcode C++题解之第26题删除有序数组中的重复项

题目&#xff1a; 题解&#xff1a; class Solution { public:int removeDuplicates(vector<int>& nums) {int n nums.size();if (n 0) {return 0;}int fast 1, slow 1;while (fast < n) {if (nums[fast] ! nums[fast - 1]) {nums[slow] nums[fast];slow;}f…

【MYSQL】MySQL整体结构

无论你是前端还是后端&#xff0c;只要是一个合格的开发者&#xff0c;对于MySQL这个名词相信都不陌生&#xff0c;MySQL逐渐成为了最受欢迎的关系型数据库&#xff0c;无论你是大前端&#xff0c;亦或是Java、Go、Python、C/C、PHP....等这些语言的程序员&#xff0c;对于MySQ…

大数据入门之如何利用Phoenix访问Hbase

在大数据的世界里&#xff0c;HBase和Phoenix可谓是一对黄金搭档。HBase以其高效的列式存储和强大的数据扩展能力&#xff0c;成为大数据存储领域的佼佼者&#xff1b;而Phoenix则以其SQL化的操作方式&#xff0c;简化了对HBase的访问过程。今天&#xff0c;就让我们一起看看如…

是时候开启Copilot下一篇章:Microsoft AI

微软总裁兼首席执行官萨提亚纳德拉欢迎 Mustafa Suleyman 和 Karn Simonyan 加入微软公司&#xff0c;领导一个新成立的部门 —— Microsoft AI&#xff0c;旨在开发 Copilot 和其他的面向消费者的 AI 产品和研究。 Mustafa Suleyman 将担任 Microsoft AI 执行副总裁&#xf…

Freecad参数化三维建模的趋势——水利水电设计

最近以chatgpt3.5彻底放开和Kimi小程序如此方便使用而火爆。 三维参数化模型是一个趋势&#xff0c;特别对于这些常规的建筑物设计&#xff0c;基本极少各种曲线曲面&#xff0c;所以特别适合做参数化。 而水利水电工程上应用的设备和产品&#xff0c;也可以建立参数化库&…

展厅装修设计中合理的使用颜色

一、选择合适的主题是最重要的一点 一个引人注目的主题可以立即吸引到游客的注意力&#xff0c;成功的将展厅的主题和产品联系在一起。这个主题应该是与你的产品或服务密切相关的&#xff0c;同时又足够具有吸引力&#xff0c;以吸引消费者对你的展厅产生好奇心。 二、合理的使…

动态规划——记忆化搜索DP

以901. 滑雪 - AcWing题库为例 记忆化搜索和DFS&#xff1a; DFS&#xff1a;在某个方向上滑雪滑倒不能滑为止&#xff0c;然后再回溯继续滑&#xff0c;直到以所有点为起始点全部遍历完 记忆化搜索&#xff1a;用f[i,j]记录&#xff0c;以某点开始滑的最大路径&#xff0c;保证…

【YUNBEE云贝-进阶课】MySQL8.0性能优化实战培训

众多已经学习过MySQL 8.0 OCP认证专家的课程的同学们对 MySQL 8.0 的安装部署、体系结构、配置监控、用户管理、主从复制、系统运维、MGR等基础操作和动手实验有了一定的学习基础.很多学员反馈希望更进一步提升技术能力、解决工作中碰到的性能问题。 针对MySQL8.0的数据库性能优…

JetBrains PhpStorm 2024.1 发布 - 高效智能的 PHP IDE

JetBrains PhpStorm 2024.1 发布 - 高效智能的 PHP IDE 请访问原文链接&#xff1a;JetBrains PhpStorm 2024.1 (macOS, Linux, Windows) - 高效智能的 PHP IDE&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org JetBrains PhpSt…

构建智能连接的未来:物联网平台系统架构解析

随着科技的不断进步和互联网的普及&#xff0c;物联网&#xff08;Internet of Things, IoT&#xff09;已成为连接世界的新方式。物联网平台作为实现物联网应用的核心基础设施&#xff0c;其系统架构的设计和实施至关重要。本文将深入探讨物联网平台系统架构的关键要素和最佳实…

建造者模式:构造复杂对象的艺术

在面向对象的设计中&#xff0c;建造者模式是一种重要的创建型设计模式&#xff0c;专门用来构建复杂的对象。它主要目的是将对象的构造代码与其表示代码分离&#xff0c;使同样的构建过程可以创建不同的表示。本文将详细介绍建造者模式的定义、实现、应用场景以及优缺点&#…

EasyConnect初始化失败如何解决?

使用EasyConnect for mac的用户是不是会经常出现这样的提示&#xff1a;“初始化失败&#xff0c;请尝试重新安 装”&#xff1f; 重新下载安装后&#xff0c;第一次使用是没有问题的&#xff0c;但是第二次使用还是会出现这样的情况。 那么怎么一劳永逸地解决这个问题呢&am…

FluentUI系列 - 1 - 介绍第一个窗口

介绍一个QML的UI库&#xff0c;国人编写&#xff0c;作者也耍知乎。这个UI库确实好用&#xff0c;但是教程基本等于无&#xff0c;个人在使用中顺便记录一下学习内容。这玩意儿也有Pyside6的版本&#xff0c;有需要的可以查看PySide6-FluentUI-QML。 FluentUI库地址​github.c…

SpringMVC--02--上下文工具类(RequestContextHolder)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 RequestContextHolder背景1.RequestContextHolder的使用2.request和response怎么和当前请求挂钩?3.request和response等是什么时候设置进去的? 案例应用---用户信…

softmax回归:多分类问题的解码器

随着人工智能技术的不断发展&#xff0c;分类问题在机器学习领域中的地位日益凸显。在众多分类算法中&#xff0c;softmax回归以其独特的优势和广泛的应用场景&#xff0c;成为了处理多分类问题的有力工具。本文将深入探讨softmax回归的原理、应用及其优缺点&#xff0c;以期为…

【好用】推荐10套后端管理系统前端模板

后台管理系统前端模板是开发者在构建后台管理系统时使用的一种工具&#xff0c;它提供了预先设计好的界面和组件&#xff0c;以帮助开发者快速搭建出功能完善、用户体验良好的管理系统。以下是V哥整理的10款流行的后台管理系统前端模板&#xff0c;它们基于不同的技术栈和设计理…

找出mongodb的jumbo块并进行分裂

https://www.cnblogs.com/abclife/p/15968628.html 根据这篇文档中的脚本&#xff0c;在我们自己的环境中跑了下&#xff0c;第一次跑的结果如下&#xff1a; 运行完上面跑出的split脚本后&#xff0c;还是存在jumbo块&#xff0c;第二次跑出的结果&#xff1a; 从上面结果可以…

【Vue3进阶】- 第2学堂小商城项目后端准备和接口文档

简介 在大多数前端项目开发中&#xff0c;都需要与后端进行接口交互&#xff0c;后端通常会以文档的形式提供接口信息&#xff0c;前端开发者通过阅读这些文档&#xff0c;了解后端接口的功能和使用方法&#xff0c;从而实现数据的获取和提交等功能。 第二学堂小商城教程后端…