<JavaEE> 经典设计模式之 -- 单例模式(“饿汉模式”和“懒汉模式”实现单例模式)

目录

一、单例模式概述

二、“饿汉模式”实现单例模式

三、“懒汉模式”实现单例模式

3.1 单线程下的“懒汉模式”

3.2 多线程下的“懒汉模式”


一、单例模式概述

1)什么是单例模式?

单例模式是一种设计模式。

单例模式可以保证某个类在程序中只存在唯一实例,即不允许创建多份实例。

使用单例模式,上述要求就得到了检查和校验。

2)单例模式的实现形式

单例模式可以通过很多种方法实现,“饿汉模式”和“懒汉模式”是其中最基础的两种,本文只介绍这两种实现。

二、“饿汉模式”实现单例模式

通过代码演示“饿汉模式”实现的单例模式:

class Singleton{//新建一个唯一实例;private static Singleton instance = new Singleton();//方法返回唯一实例;public static Singleton getInstance() {return instance;}//将构造方法私有化;private Singleton() { }
}
1)上述代码做了什么?

创建了一个被 static 修饰的实例,这个实例成为了类属性。类对象只会有一个,这个类属性也只会有一个。

私有化构造方法,外部无法 new 新的实例,只能通过 get 方法获取唯一的那一个 instance。
2)为什么叫做“饿汉模式”?

上述代码中,实例是类属性。类属性在类加载的时候就创建了,创建时机早,十分“迫切”,因此称为“饿汉模式”。

代码证明“饿汉模式”返回的实例是唯一的:

public class Singleton_Demo0 {public static void main(String[] args) {//想直接new对象,就会报错;//Singleton instance = new Singleton();//两次调用getInstance()方法并分别赋值;Singleton instance1 = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();//对比两个变量,发现是同一实例;if(instance1 == instance2){System.out.println("两个对象是同一个对象");}}
}//运行结果:
两个对象是同一个实例
3)“饿汉模式”的单例模式在多线程下是线程安全的吗?

上述代码中,get 方法返回的是已经创建好的实例,这个操作本质上只是一个“读操作”,多个线程读取同一个变量并不会造成线程不安全。

因此“饿汉模式”的单例模式在多线程下是线程安全的。

三、“懒汉模式”实现单例模式

3.1 单线程下的“懒汉模式”

通过代码演示懒汉模式现的单例模式:

class Singleton{//声明一个变量作为类属性;private static Singleton instance = null;//判断变量是否为null,是则创建实例后返回,否则返回;public static Singleton getInstance() {if(instance == null){instance = new Singleton();}return instance;}//将构造方法私有化;private Singleton() { }
}
1)上述代码做了什么?

声明了一个类属性。类对象只会有一个,这个类属性也只会有一个。

私有化构造方法,外部无法 new 新的实例,只能通过 get 方法获取唯一的那一个 instance。
get 方法中根据变量是否为 null 判断是否应该创建实例。
2)为什么叫做“懒汉模式”?

上述代码中,实例是在程序员第一次调用 get 方法后才创建的,创建时机较晚,或者根本不用创建,因此称为“懒汉模式”。

3.2 多线程下的“懒汉模式”

1)单线程下的“懒汉模式”在多线程下是线程安全的吗?

答案是否定的,单线程下的“懒汉模式”在多线程下是线程不安全的,我们可以从以下两个方面分析:

“原子性”:

上述代码中判断变量是否为空的代码 —— if(instance == null),和实例化代码 —— 

instance = new Singleton(),并非是“原子”的。在多线程环境下,这就可能导致线程不安全。

可以使用 synchronized 关键字,将这两句代码加锁,解决这个问题。

内存可见性和指令重排序:

因为 instance 是一个被 static 修饰的共享数据,而且编译器内部可能对实例化的代码 —— new Singleton(),进行了编译器优化。

这就无法保证内存的可见性和指令的顺序执行,因此在多线程环境下可能导致线程不安全。

可以使用 volatile 关键字,对共享数据 instance 进行修饰,解决这个问题。

使用以上两个关键字的原因和方式,详细请参考以下博客:

阅读指针 -> 《synchronized 关键字 和 volatile 关键字》<JavaEE> synchronized关键字和锁机制 -- 锁的特点、锁的使用、锁竞争和死锁、死锁的解决方法-CSDN博客文章浏览阅读70次。介绍了 synchronized 关键字 和 锁机制,其中重点介绍了锁的特点、使用方法和死锁的相关内容。https://blog.csdn.net/zzy734437202/article/details/134742168<JavaEE> volatile关键字 -- 保证内存可见性、禁止指令重排序-CSDN博客文章浏览阅读59次。简单介绍什么是内存可见性和指令重排序。volatile关键字可以将这两种编译器优化强制关闭。https://blog.csdn.net/zzy734437202/article/details/134757070

2)“懒汉模式”在多线程下应该怎么编写?

根据上述分析,根据单线程模式下的“懒汉模式”进行改进。

方法如下:

增加 volatile 关键字对共享数据进行修饰。

为判断是否为 null 和 实例化的代码加锁,使这两句代码称为“原子”。

增加 volatile 关键字对共享数据进行修饰:

private volatile static Singleton instance = null;

为判断是否为 null 和 实例化的代码加锁,使这两句代码称为“原子”:

    public static Singleton getInstance() {synchronized (locker){if(instance == null){instance = new Singleton();}}return instance;}
3)“双重校验锁”

我们再仔细分析一下上述的 get 方法。

假设程序需要多次调用这个 get 方法,那么每一次进入都会进行加锁,加锁是会增加系统开销的。

那么是否真的有必要每次都加锁呢?

当 get 方法被第一次调用,实例就会被创建,那么后续再调用这个 get 方法时,返回实例就好了,加锁部分的代码块,完全可以不用执行。

在加锁的代码块之外,再增加一个if(instance == null)进行判断,那么实例在被创建之后,也就不会再进入加锁的代码块中了。

我们成功利用“双重校验锁”,优化了程序。

代码演示“双重校验锁”优化后的 get 方法:

    public static Singleton getInstance() {//这个if用于判断是否需要加锁;if(instance == null){synchronized (locker){//这个if用于判断是否需要新建实例;if(instance == null){instance = new Singleton();}}}return instance;}

经过以上的完善和优化,我们终于可以写出在多线程下保证线程安全的“懒汉模式”单例模式了:

class Singleton{//声明一个变量作为类属性;private volatile static Singleton instance = null;private static final Object locker = new Object();//判断变量是否为null,是则创建实例后返回,否则返回;public static Singleton getInstance() {//这个if用于判断是否需要加锁;if(instance == null){synchronized (locker){//这个if用于判断是否需要新建实例;if(instance == null){instance = new Singleton();}}}return instance;}//将构造方法私有化;private Singleton() { }
}

阅读指针 -> 《经典设计模式之 -- 使用阻塞队列实现“生产者-消费者模型”》

<JavaEE> 经典设计模式之 -- 使用阻塞队列实现“生产者-消费者模型”-CSDN博客自己实现了的阻塞队列,介绍了经典的设计模式“生产者-消费者模型”。https://blog.csdn.net/zzy734437202/article/details/134807241

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

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

相关文章

Qt简介、C++工程文件分离、创建Qt工程、Qt的帮助文档

QT 简介 core:核心模块,非图形的接口类,为其它模块提供支持 gui:图形用户接口,qt5之前 widgets:图形界面相关的类模块 qt5之后的 database:数据库模块 network:网络模块 QT 特性 开…

dll动态链接库【C#】

1说明: 在C#中,dll是添加 【类库】生成的。 2添加C#的dll: (1)在VS中新建一个Windows应用程序项目,并命名为TransferDll。 (2)打开Windows窗体设计器,从工具箱中为窗体…

数据结构--二叉树

目录 1.二叉树链式结构的实现 1.1 前置说明 1.2 二叉树的遍历 1.2.1 前序、中序以及后序遍历 1.2.2 层序遍历及判断是否为完全二叉树 1.3 节点个数,叶子节点个数,第k层节点个数以及高度等 1.4 二叉树的创建和销毁 1.二叉树链式结构的实现 1.1 前置说…

flask web开发学习之初识flask(三)

文章目录 一、flask扩展二、项目配置1. 直接配置2. 使用配置文件3. 使用环境变量4. 实例文件夹 三、flask命令四、模版和静态文件五、flask和mvc架构 一、flask扩展 flask扩展是指那些为Flask框架提供额外功能和特性的库。这些扩展通常遵循Flask的设计原则,易于集成…

如何在Word中简洁地插入代码

如何在Word中简洁地插入代码 背景: ​ 最近在一写一些论文或者报告的时候,需要将源代码放在论文的最后,有一个很头疼的问题,如果直接把代码从编辑器复制到word中,就变成了下面这个样子: 这有点丑陋啊&…

【快速应用开发】看看RedwoodJS

自我介绍 做一个简单介绍,酒架年近48 ,有20多年IT工作经历,目前在一家500强做企业架构.因为工作需要,另外也因为兴趣涉猎比较广,为了自己学习建立了三个博客,分别是【全球IT瞭望】,【…

大数据分析与应用实验任务十一

大数据分析与应用实验任务十一 实验目的 通过实验掌握spark Streaming相关对象的创建方法; 熟悉spark Streaming对文件流、套接字流和RDD队列流的数据接收处理方法; 熟悉spark Streaming的转换操作,包括无状态和有状态转换。 熟悉spark S…

极简模式,助力宏观数据监控

随着UWA GOT Online采样的参数越来越多样化,为了提升开发者的使用体验,我们最新推出了三种预设数据采集方案:极简模式、CPU模式、内存模式。该更新旨在降低多数据采集对数据准确性的干扰,同时也为大家提供更精准且有针对性的数据指…

mac苹果笔记本电脑如何强力删除卸载app软件?

苹果电脑怎样删除app?不是把app移到废纸篓就行了吗,十分简单呢! 其实不然,因为在Mac电脑上,删除应用程序只是删除了应用程序的主要组件。大多数时候,系统会有一个相当长的目录,包含所有与应用程…

vuepress-----22、其他评论方案

vuepress 支持评论 本文讲述 vuepress 站点如何集成评论系统,选型是 valineleancloud, 支持匿名评论,缺点是数据没有存储在自己手里。市面上也有其他的方案, 如 gitalk,vssue 等, 但需要用户登录 github 才能发表评论, 但 github 经常无法连接,导致体验…

搞定这些软件测试面试题,面试通过率提高百分之80!

十九、持续集成 19.1 jenkins ant jmeter svn 接口自动化测试? jenkins ant jmeter svn 环境搭建 原来这个环境是我这边搭建的, 主要是几个步骤, 第一 Jenkins 安装、第二,ant 安装、第三, jmeter 安装、第四, …

关于加密解密,加签验签那些事

面对MD5、SHA、DES、AES、RSA等等这些名词你是否有很多问号?这些名词都是什么?还有什么公钥加密、私钥解密、私钥加签、公钥验签。这些都什么鬼?或许在你日常工作没有听说过这些名词,但是一旦你要设计一个对外访问的接口&#xff…

API测试基础之http协议

http简介: http(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP(传输控制协议)之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII码形式给出…

生成式人工智能笔记-AIGC笔记

生成式人工智能笔记-AIGC笔记 十多年前,人工智能还只是一个不被人看好的小众领域,但是现在,它却已经成了街头巷尾的热点谈资,几乎任何事情都可以和人工智能联系在一起。 人工智能包括基础层、技术层和应用层。 基础层是人工智能…

WebRTC AEC回声消除算法拆解

WebRTC AEC算法流程分析——时延估计(一) 其实,网上有很多类似资料,各个大厂研发不同应用场景设备的音频工程师基本都对其进行了拆解,有些闪烁其词,有些却很深奥,笔者随着对WebRTC了解的深入&a…

scripty妙用

在monorepo项目中,随着子模块增多, 每个子项目都需要配置各自的package.json,并且大同小异,为了进一步提高配置效率,引入了scripty,自己写脚本,直接就可以用哦 1、安装 npm install scripty --save-dev 2…

实现安装“自由化”!在Windows 11中如何绕过“您尝试安装的应用程序未通过微软验证”

这篇文章描述了如果你不能安装应用程序,而是当你在Windows 11中看到消息“您尝试安装的应用程序未通过微软验证”时该怎么办。完成这些步骤将取消你安装的应用程序必须经过Microsoft验证的要求。 使用设置应用程序 “设置”应用程序提供了绕过此警告消息的最简单方法,以便你…

基于JavaWeb+SSM+Vue马拉松报名系统微信小程序的设计和实现

基于JavaWebSSMVue马拉松报名系统微信小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.…

记录 | vscode设置自动换行

右上菜单栏 -> 查看 -> 打开自动换行 或者还有种方式,如下, 左下角小齿轮,点击设置 然后输入 Editor: Word Wrap ,把开关打开为 on

微信小程序 长按录音+录制视频

<view class"bigCircle" bindtouchstart"start" bindtouchend"stop"><view class"smallCircle {{startVedio?onVedio:}}"><text>{{startVedio?正在录音:长按录音}}</text></view> </view> <…