浅谈操作系统中的重要概念——线程(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…

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方式来实现…

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

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

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图表集&…

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

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

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组件—远程桌面服务—远程桌面会话主机----连…

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

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

Semi-decentralized Federated Ego Graph Learning for Recommendation

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

zabbix监控方式(zabbix-trapper)

中文&#xff1a;zabbix采集器&#xff0c;即zabbix sender 。 Zabbix-Trapper 监控方式可以一次批量发送数据给Zabbix Server&#xff0c;与主动模式不同&#xff0c;Zabbix-Trapper 可以让用户控制数据的发送&#xff0c;而不用Zabbix-Agent进程控制&#xff0c;这意味着可以…

PE文件(四)FileBuffer-ImageBuffer

文件执行的总过程 当文件从硬盘中读入虚拟内存&#xff08;FileBuffer&#xff09;中时&#xff0c;文件数据会被原封不动的复制一份到虚拟内存中&#xff0c;然后进行拉伸对齐。此时虚拟内存中文件数据叫做文件印象或者内存印象&#xff0c;即ImageBuffer。此时ImageBuffer中…

42.乐理基础-拍号-看懂拍号的意义

到这必然是已经知道 X、Y的意思了&#xff1a; 然后带入数字&#xff1a; 然后念拍号的时候&#xff0c;在国内&#xff0c;百分之九十的地方是从下往上念&#xff0c;念作四二拍&#xff0c;还有百分之十的地方是和国外一样&#xff0c;从上往下念&#xff0c;念作二四拍&…

跨境支付行业研究

1. 行业基本情况 随着全球人均购买力增强、互联网普及率提升、支付渠道的进一步成熟、物流等配套设施的完善&#xff0c;网络购物已经成为全球兴起的消费习惯。另一方面&#xff0c;跨境电商对传统贸易的替代已经成为趋势。跨境电商在交易成本和便利程度上都有明显的优势 图1 …

大数据API技术分享:使用API接口采集淘宝数据(商品详情丨关键词搜索丨店铺所有商品)

使用API接口采集淘宝数据&#xff08;商品详情、关键词搜索、店铺所有商品&#xff09;是大数据领域常见的应用场景。以下是一些关于如何使用API接口进行这些操作的技术分享&#xff1a; 1. 获取API权限 首先&#xff0c;你需要在淘宝开放平台注册成为开发者&#xff0c;并创建…

想做视频号小店,为何不建议开通个体店?开店步骤+做店思路如下

我是王路飞。 如果你想在视频号开通店铺的话&#xff0c;那么一定不要使用个体执照开通个体店&#xff1f; 这是为什么呢&#xff1f; 原因很简单&#xff0c;视频号个体店是无法入驻优选联盟的&#xff0c;只能企业店可以入驻。 因为现阶段视频号小店的自然流量很少&#…

五一 作业

#include <iostream>using namespace std; class Num { private:int a; public:Num() {}Num(int a):a(a){}//设置a的值void set(int a){this->aa;}//1-a的和void Sum(){if(a<1){cout<<"a<1"<<endl;return;}int sum0;for(int i1;i<a;i)…

开源模型应用落地-CodeQwen模型小试-探索更多使用场景(三)

一、前言 代码专家模型是基于人工智能的先进技术&#xff0c;它能够自动分析和理解大量的代码库&#xff0c;并从中学习常见的编码模式和最佳实践。这种模型可以提供准确而高效的代码建议&#xff0c;帮助开发人员在编写代码时避免常见的错误和陷阱。 通过学习代码专家模型&…

计算机视觉——OpenCV Otsu阈值法原理及实现

算法简介 Otsu阈值法&#xff0c;也被称为大津算法&#xff0c;是一种在图像处理中广泛使用的自动阈值分割技术。这种方法由日本学者大津展之于1979年提出&#xff0c;旨在根据图像的灰度直方图来自动选择最佳全局阈值。Otsu阈值法的核心思想是最小化类内方差或最大化类间方差…