浅谈操作系统中的重要概念——线程(3)——设计模式

文章目录

  • 一、什么是设计模式?
  • 二、单例模式
    • 2.1、饿汉模式
    • 2.2、懒汉模式
    • 2.3、多线程情况下调用 饿汉模式与懒汉模式 谁是安全的??(重点)
  • 三、工厂模式

一、什么是设计模式?

设计模式就相当于菜谱,有了菜谱/秘籍,就能够根据菜谱上的指引/步骤做出许多从前不会的美食,就算不会下厨的人,拥有了食谱,他的厨艺也能够得到提升和保障。

因此设计模式就是程序员的菜谱,设计模式中介绍了许多典型场景,以及针对这些典型场景的处理办法。按照设计模式来写的代码不会很差,在一定的规范范围里。

设计模式有很多种,不止23种,今天主要介绍两种常见、常用的:
1、单例模式
2、工厂模式

二、单例模式

单例模式对应的场景:希望代码中的有些对象在整个程序中只有一个实例(即对象只能 new 一次)。

譬如说:JDBC中的数据源DataSource这样的对象就只需要一个即可,因此这个对象就可以使用单例模式。

对于我们程序员来说,如果有些对象在整个程序中只需要有一个实例即可,那我们程序员就只new一次就好了,为什么还需要引入单例模式??那是因为对于程序来说,人是不靠谱的,就需要引入单例模式,此时由编译器来进行严格的检查(对于代码中只能new一次的对象,如果尝试new了多次,编译器就会直接报错来进行提示),确保某些对象处于单例模式。

接下来介绍一下在Java中单例模式的两种写法:

2.1、饿汉模式

饿汉模式:程序启动进行类加载之后,立即创建出实例。(比较迫切)

代码例子:

class Singleton{/*** 此处就期望类Singleton只有一个实例*///    静态成员:instance
//    static  静态的,与类相关的,整个程序中只有一份private static Singleton instance = new Singleton();//    通过此方法,随时获取到刚刚的 instance 变量public static Singleton getInstance(){return instance;}//    作出限制:禁止别人去 new 这个类的实例——>将构造方法变成privateprivate Singleton(){}
}public class testHunger {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();
//        此时我们再new类的实例,直接编译报错(编译器校验了)
//        Singleton s3 = new Singleton();//        我们可以发现 s1、s2 这两个都是同一个对象System.out.println(s1.equals(s2)); //true
//        s1、s2 和 s3 不是同一个对象
//        System.out.println(s3.equals(s1)); //false
//        System.out.println(s3.equals(s2)); //false}
}

但是上述代码中,我们将类的构造方法改成private,此时别人就一定不能创建多个实例了吗??其实还是可以通过 反射机制 ,在当前单例模式中,创建出多个实例。
在这里插入图片描述但是反射属于 “非常规” 的编程手段,正常开发的时候,慎重使用,因为 滥用反射,会给代码带来极大风险,譬如会让代码变得抽象,日后难以维护,还破坏了Java的封装性。

当然Java中也有其他方式书写单例模式来防止反射,但此处不做过多介绍,可以自行查阅资料了解。

2.2、懒汉模式

懒汉模式:第一次使用实例的时候才创建实例,否则能不创建就不创建。(比较佛系)

/*** 代码示例:* 单例模式中的懒汉模式*/
class SingletonLazy{private static SingletonLazy instance = null;public static SingletonLazy getInstance(){if(instance == null){instance = new SingletonLazy();}return instance;}private SingletonLazy(){}
}public class testLazy {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();//        同样也是可以通过非常规手段:反射机制 获取类的实例Class<SingletonLazy> s3 = SingletonLazy.class;System.out.println(s3.equals(s1));System.out.println(s1.equals(s2));}
}

2.3、多线程情况下调用 饿汉模式与懒汉模式 谁是安全的??(重点)

饿汉模式:
在这里插入图片描述

懒汉模式:
在这里插入图片描述
在这里插入图片描述
上述逻辑,也是一个经典的面试题!

那我们如何保证懒汉模式的线程安全呢?

由于上述引起线程安全的原因是 if语句与new语句不是一个整体,因此出现了线程安全问题,此时我们通过将if语句和new语句打包成一个整体来解决懒汉模式的线程安全问题。

在这里插入图片描述
加锁之后,线程在cpu上的执行流程:(当加了锁之后,线程t2执行if语句时,就会发现此时的instance是一个非null的值,)
在这里插入图片描述
虽然加了锁,解决了线程安全问题,但是还存在问题:加锁是一个高成本的操作,会引起阻塞等待。加锁的基本原则应该是:非必要,不加锁。不能无脑加锁,如果无脑加锁,就会导致程序的执行效率受到影响。

此时我们对懒汉模式的代码中加的锁,导致后续每次调用 getInstance() 方法都要加锁,但是这是不合理且不必要的。**懒汉模式线程不安全主要是因为首次 new 对象时,才存在的。一旦将对象 new 出来后,后续再调用 getInstance() 方法就不存在线程安全问题了。**但是我们现在的加锁是:首次 new 对象调用时,加锁了。后续调用,也加锁了。但实际上后续调用不必加锁,因为后续调用后if条件就进不去了,此时也就不再涉及到修改操作了,全是读操作。但我们把不该加锁的地方加上锁了,很影响程序的执行效率。

对加锁操作做出以下修改:
在这里插入图片描述

两个if语句之间存在加锁操作,加锁就会引起线程阻塞等待,究竟阻塞等待多久,不知道,有可能第一个if语句与第二个if语句间隔沧海桑田,因此在这个长久的时间间隔里,可能别的线程就把instance改了。

上述对加锁操作做了修改之后,还存在一个问题:
在这里插入图片描述
已知一般 instance = new SingletonLazy();可以大致分成3个步骤:
1、给对象创建出内存空间,得到内存地址。
2、在空间上调用构造方法,对对象进行初始化。
3、把内存地址赋值给 instance引用。
在这里插入图片描述

假设现在代码的执行顺序由123变成132,但是在执行步骤3之后,进行了线程切换,此时还没来得及执行步骤2,即给对象初始化,就调度给别的线程了,此时别的线程执行的时候,判断instance不为空了,于是就直接返回instance,并且后续代码中可能会使用 instance 中的一些属性或者方法,但是由于多线程下出现了指令重排序的问题导致线程安全问题,此时的instance是个空引用,即拿到的这个对象,是个没有进行初始化,不完整的对象。

解决办法:给变量 instance 加上 volatile 之后,此时针对 instance 的赋值操作,就不会产生上述的指令重排序了,其执行顺序必然是遵循1、2、3执行。

加上 volatile 还有一个另外的用途:避免此处赋值操作的指令重排序。

懒汉模式的最终代码版本:

/*** 代码示例:* 单例模式中的懒汉模式*/
class SingletonLazy{private static volatile SingletonLazy instance = null;public static SingletonLazy getInstance(){if (instance == null){synchronized (SingletonLazy.class){if(instance == null){instance = new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}public class testLazy {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();//        同样也是可以通过非常规手段:反射机制 获取类的实例Class<SingletonLazy> s3 = SingletonLazy.class;System.out.println(s3.equals(s1));System.out.println(s1.equals(s2));}
}

懒汉模式代码的3大要点:
1、加锁
2、双重if
3、volatile

从字面上理解/区分 饿汉模式和懒汉模式 会比较抽象,举2个例子加深一下理解:
譬如说日常生活中,吃完饭之后需要洗碗,有的人习惯吃完饭后立即洗碗,有的人习惯吃完饭后将碗放到一边,等到下顿饭要吃的时候才洗碗。夜宵大家会觉得吃完饭之后立即洗碗是一种高效率的生活方式,但其实在不考虑卫生的情况下,吃完饭后不洗碗,等到下次开饭再洗碗然后接着用,其实效率会更高。比如说这顿吃完有4个碗,下顿开饭只需要用到2个碗,此时就只需要洗2个碗来用就行了,效率大大提高了。

还譬如说:假设编辑器打开一个很大的文件,有的编辑器会一下尝试把所有内容都加载到内存中,再显示出来,这是典型的饿汉模式。有的编辑器,则只加载一部分内容(一个屏幕能显示的内容),其他部分,当用户翻页想要浏览时,再加载出来,这是典型的懒汉模式。

三、工厂模式

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

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

相关文章

虚拟机文件夹共享操作(本地访问)

新建一个文件夹 右击文件夹点击属性 找到共享 点击共享 选择本地用户共享就可以了 本地winr 输入我们图片中的格式&#xff08;IP前加 “\\” &#xff09; 会弹一个窗口&#xff0c;输入虚拟机的入户名和密码就可以共享了&#xff08;一般默认用户名都是administrator&am…

2.3.1 语言大模型的预训练

模块化大模型 接口安全&#xff1a; 模块化设计意味着不同的模块需要通过定义良好的接口进行交互。如果这些接口没有得到适当保护&#xff0c;它们可能成为攻击者的攻击目标&#xff0c;攻击者可以通过这些接口注入恶意数据或提取敏感信息。 数据泄露风险&#xff1a; 在模块化…

C++笔记:类与对象(三)->多态

多态 虚函数 先来看一段代码&#xff1a; #include<iostream> using namespace std;class Animal { public :void run() {cout << "I dont know how to run" << endl;} };class Cat : public Animal{ public :void run() {cout << "I …

【毕业设计】基于微信小程序的校园快递平台系统设计与实现

1.项目介绍 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统校园快递平台系统信息管理难度大&#xff0c;容错率…

让我们把Domino变成SFTP服务器

大家好&#xff0c;才是真的好。 远程共享文件有很多办法&#xff0c;其中值得注意的是SFTP方式。SFTP即SSH文件传输协议&#xff0c;通过使用SSH传输层&#xff0c;SFTP可以通过Internet连接安全地访问和移动大量数据文件。 今天我们就介绍使用Domino中的HTTP OSGI方式来实现…

AI代理:未来技术的革命者

在人工智能的众多分支中&#xff0c;AI代理&#xff08;AI Agent&#xff09;无疑是最具变革性的一环。这种高级软件系统能自主完成任务&#xff0c;模仿人类决策过程来优化和执行复杂的工作流程。 它们的智能不仅限于执行固定指令&#xff0c;更能理解并智能响应复杂环境。从…

如何自己快速的制作流程图?6个软件教你快速进行流程图制作

如何自己快速的制作流程图&#xff1f;6个软件教你快速进行流程图制作 自己制作流程图可以是项目管理、流程设计或教学展示中的重要环节。以下是六款常用的流程图制作软件&#xff0c;它们都提供了快速、简单的方式来制作流程图&#xff1a; 迅捷画图&#xff1a;这是一款非…

Git 常用操作记录

keywords: Git, 版本控制 常用命令 功能命令添加文件/更改到暂存区git add filename添加所有文件/更改到暂存区git add .提交git commit -m msg从远程仓库拉取最新代码git pull origin master推送到远程仓库git push origin master查看配置信息git config --list查看文件列表…

【TensorFlow深度学习】加载经典数据集MNIST的实战步骤

加载经典数据集MNIST的实战步骤 1. 数据集概览2. 自动加载MNIST数据集3. 数据预处理4. 转换为TensorFlow Dataset对象5. 数据集的随机打散与批量处理6. 数据预处理函数的应用结语 在深度学习的实践中&#xff0c;加载经典数据集是入门及进阶学习不可或缺的一环。MNIST数据集作为…

Springboot集成feign远程调用

需求&#xff1a;在leadnews-wemedia微服务里需要调用leadnews-article微服务的接口。新建一个支持feign调用的名为heima-leadnews-feign-api的模块 heima-leadnews-feign-api的pom文件里导入openfeign依赖 <dependency><groupId>org.springframework.cloud</g…

echarts学习笔记:柱状图+雷达图+双环形图+地图可视化+数据传递关系图+关键词条图+数据总览图+AntV/G2/DataV

GitHub - lgd8981289/imooc-visualization: https://www.bilibili.com/video/BV1yu411E7cm/?vd_source391a8dc379e0da60c77490e3221f097a 课程源码 国内echarts镜像站&#xff1a;ISQQW.COM x ECharts 文档&#xff08;国内同步镜像&#xff09; - 配置项 echarts图表集&…

openGauss学习笔记-277 openGauss性能调优-实际调优案例06-改写SQL消除子查询(案例1)

文章目录 openGauss学习笔记-277 openGauss性能调优-实际调优案例06-改写SQL消除子查询(案例1)277.1 现象描述277.2 优化说明openGauss学习笔记-277 openGauss性能调优-实际调优案例06-改写SQL消除子查询(案例1) 277.1 现象描述 select 1,(select count(*) from normal_d…

618挑选家用洗地机,需要注意哪些事项?有哪些家用洗地机值得买?

近年来&#xff0c;智能清洁家电越来越受到消费者的欢迎&#xff0c;洗地机作为清洁家电的新宠&#xff0c;凭借其集扫地、拖地、杀菌清洗于一体的强大功能&#xff0c;成为市场上的热销产品。那么&#xff0c;这类洗地机真的好用吗&#xff1f;怎么挑选到好用的家用的洗地机呢…

Java基础学习:Dozer映射框架

文章目录 一、Dozer 介绍二、Dozer的API映射方式三、使用1、java程序2、springboot项目&#xff08;1&#xff09;引入依赖&#xff08;2&#xff09;声明工具类&#xff08;3&#xff09;工具类委托spring管理 一、Dozer 介绍 Dozer是一个Java工具&#xff0c;主要用于在相同…

win10下,svn上传.so文件失败

问题&#xff1a;win10下使用TortoiseSVN&#xff0c;svn上传.so文件失败 解决&#xff1a;右键&#xff0c;选择Settings&#xff0c;Global ignore pattern中删除*.so&#xff0c;保存即可。

设置多用户远程登录windows server服务器

##设置多用户远程登录windows server服务器 ###1、远程登录windows server 2016 运行—>mstsc—>远程IP地址—>用户和密码 2、远程windows服务器设置多用户策略 运行—>gpedit.msc->计算机配置—管理模板—windows组件—远程桌面服务—远程桌面会话主机----连…

Unity调用智谱API(简单操作 文本实时翻译)

代码展示&#xff1a; using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI;public class ZhiPuAi : MonoBehaviour {// API的端点URLpublic string…

linux复习

与"区别 在Linux中&#xff0c;单引号&#xff08;&#xff09;和双引号&#xff08;"&#xff09;用于定义字符串&#xff0c;但它们在处理变量扩展和转义字符时有所不同。以下是具体分析&#xff1a; 1. **变量扩展**&#xff1a;在双引号内&#xff0c;变量可以…

阿里巴巴1688商品详情API返回值深度剖析:精准获取商品信息的关键

在电子商务日益繁荣的今天&#xff0c;阿里巴巴1688作为中国领先的B2B平台&#xff0c;汇聚了海量的供应商和商品信息。对于商家、开发者以及希望深入了解商品数据的用户来说&#xff0c;如何通过有效的方式获取这些商品信息成为了一个重要议题。阿里巴巴1688商品详情API的出现…

Semi-decentralized Federated Ego Graph Learning for Recommendation

论文概况 本文是2023年WWW的一篇联邦推荐论文&#xff0c;提出了一个半去中心化的联合自我图学习框架。 Introduction 作者提出问题 现有的推荐方法收集所有用户的自我图来组成一个全局图&#xff0c;导致隐私风险。联合推荐系统已被提出来缓解隐私问题&#xff0c;但在客户…