当前位置: 首页 > news >正文

JavaEE——单例模式

文章目录

  • 一、介绍什么是单例模式
  • 二、饿汉模式
  • 三、懒汉模式
  • 四、讨论两种模式的线程安全问题

一、介绍什么是单例模式

在介绍单例模式之前,我们得先明确一个名词设计模式

所谓设计模式其实不难理解,就是在计算机这个圈子中,呢些大佬们为了防止我们这些资质平平的程序猿不把代码写的太差,所设计出的针对一些场景的问题解决方案,解决框架。

单例模式就是多个设计模式中的一个。

单例模式,逐字来理解就是:单个示例对象的意思。
在某些场景中,有些特定的类只能创建出一个实例,不应该创建多个实例。这样的要求虽然可以通过程序员本人进行控制,但是仍然存在不确定性。
使用单例模式后,此时只能创建 1 个 实例,单例模式就是针对上述的特殊需求的一个强制的保证。

在 Java 中实现单例模式的方法有很多,这里介绍一下最常见的两类。
(1) 饿汉模式
(2) 懒汉模式

二、饿汉模式

我们已经知道,单例模式就是要让某个类创建出唯一一个对象,所谓饿汉,字面理解就是一个饿了很久的人,这样的人在看到吃的就会异常急切。
这里的饿汉模式,就是让代码在一开始执行的时候,即就是类加载阶段,就去创建这个类的实例,这种效果就给人一种 “非常急切” 的感觉。
下面我来展示一下相关的代码示例

//饿汉模式
//保证 Singleton 这个类只能创建一个实例
class Singleton{//在此处先将实例创建出来private static Singleton instance = new Singleton();//如果要使用这个唯一实例,同意通过 Singleton.getInstance() 方式获取public static Singleton getInstance(){return instance;}//为了避免 Singleton 类被复制多份//将构造方法设置为 private。再类外面无法通过 new 来实现创建 Singleton 实例private Singleton(){}
}public class ThreadDemo {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);}
}

在这里插入图片描述
上面的结果就表明这是同一个实例。

static 在这里的作用(了解 static 关键字作用的可以跳过)

在这里插入图片描述
static 在这里保证了使用的对象是唯一的,下面通过代码示例来解释一下:

class Test{public int A;public static int B;
}public class StaticTest {public static void main(String[] args) {Test t1 = new Test();Test t2 = new Test();//设置非 static 修饰的变量t1.A = 10;t2.A = 20;System.out.println("t1.A的值"+ t1.A);System.out.println("t2.A的值"+ t2.A);//设置由 static 修饰的变量t1.B = 10;t2.B = 20;System.out.println("t1.B的值"+ t1.B);System.out.println("t2.B的值"+ t1.B);}
}

在这里插入图片描述总的来说,被 static 修饰的关键字在代码中表现的内容与最后一次设定是一样的,这样也就确保了使用对象的唯一性。

三、懒汉模式

对于懒汉模式 “懒汉” 字面意义上不难理解,就是表示一个人很懒,直到事情迫在眉睫才会去做。
在这里,懒汉模式也是同样,在类加载之初是不会进行对象的创建,一直到第一次真正使用的时候才会去创建

代码示例:

//实现懒汉模式
class SingletonLazy{//这里先将对象不进行创建,先设定为 nullprivate static SingletonLazy instance = null;public static SingletonLazy getInstance(){//判断是否有对象被创建,没有就进行创建if (instance == null){instance = new SingletonLazy();}return instance;}//将构造方法设定为 static 不能进行 new 来创建private SingletonLazy(){}
}public class ThreadDemo21 {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);}
}

运行结果
在这里插入图片描述
同样的,这一样表示了上面运用的是同一个对象。

四、讨论两种模式的线程安全问题

上面的 饿汉模式 和 懒汉模式,在多线程的调用下是很有可能存在线程安全问题的。下面我来简单分析一下。

前面我们已经了解了关于线程安全问题的部分知识,我们知道,计算机中对数据的处理分为,1. 从内存 “读”。2.在cpu寄存器上 “改”。3. “写”入内存。这几个操作。在这里我们就根据上面几点来进行解释。

在这里插入图片描述
在这里插入图片描述
如上图所示,我们发现饿汉模式只存在这个操作,操作很单一,在这里就没有线程安全问题。懒汉模式存在着读和写两个操作,对此如果不进行约束,是有可能会出现线程安全问题。如下图所示:

在这里插入图片描述
呢么,如何才能让懒汉模式 线程安全?答案是:加锁。

class SingletonLazy{//这里先将对象不进行创建,先设定为 nullprivate static SingletonLazy instance = null;public static SingletonLazy getInstance(){//通过加锁消除线程安全问题,在此处加锁才能保证读操作和修改操作是一个整体synchronized (SingletonLazy.class){//判断是否有对象被创建,没有就进行创建if (instance == null){instance = new SingletonLazy();}    }return instance;}//将构造方法设定为 static 不能进行 new 来创建private SingletonLazy(){}
}

这里加锁之后,t1 线程已经创建了一个对象,之后的 t2 线程 load 到的结果是前面线程修改的结果。因此 t2 线程就不会在创建新的对象,直接返回现有的对象。

需要注意的是,在这里的加锁操作,在线程每进行 getInstance 操作时每次都会进行加锁,但是每次的加锁都要有开销,真的需要每次加锁吗?我们知道在创建对象前,需要进行加锁,但是,在第一次创建对象后,后续的操作会直接触发 return,对此可以对代码进行如下修改:

//实现懒汉模式
class SingletonLazy{//这里先将对象不进行创建,先设定为 nullprivate static SingletonLazy instance = null;public static SingletonLazy getInstance(){//负责判断是否要加锁if(instance == null){//通过加锁消除线程安全问题,在此处加锁才能保证读操作和修改操作是一个整体synchronized (SingletonLazy.class){//判断是否有对象被创建,没有就进行创建if (instance == null){instance = new SingletonLazy();}}}return instance;}//将构造方法设定为 static 不能进行 new 来创建private SingletonLazy(){}
}

注:上面的两个 if 条件语句虽然条件相同,但是两者之间控制的问题完全不同。

有关懒汉模式上面的加锁操作以及对加锁操作的修改仍然没有完全解决其中存在的问题。这里还存在内存可见性问题,指令重排序问题。

内存可见性问题: 在这里,假设有很多线程都去进行 getInstance,此时就很有可能有被优化的风险,只有第一次读取了真正的内存,后续读取的都是寄存器中的数据。

指令重排序问题: 在这里,我们将 instance = new SingIeton(); 拆分为下面三部分。

  • 1.申请内存空间
  • 2.调用构造方法,将内存空间初始化为一个合理的对象
  • 3.将内存地址赋值给 instance 引用。

在正常情况下,按照1,2,3这样的顺序进行执行的。但是,在多线程的环境下,就会出现问题。

假设线程 t1 是在排序后按照 1,3,2这个顺序进行执行的。当执行完 1,3 这两个步骤后,被切出 cpu ,此时 t2 进入cpu,这里就出现问题了,这里的 t2 拿到的是非法的对象,是没有构造完成的不完整对象。

要解决上面的问题 volatile关键字可以解决问题。
volatile 关键字有下面两个功能:
(1) 解决内存可见性
(2) 禁止指令重排序

代码如下:

//实现懒汉模式
class SingletonLazy{//这里先将对象不进行创建,先设定为 null//添加 volatile 关键字,防止指令重排序,解决内存可见性问题private volatile static SingletonLazy instance = null;public static SingletonLazy getInstance(){//负责判断是否要加锁if(instance == null){//通过加锁消除线程安全问题,在此处加锁才能保证读操作和修改操作是一个整体synchronized (SingletonLazy.class){//判断是否有对象被创建,没有就进行创建if (instance == null){instance = new SingletonLazy();}}}return instance;}//将构造方法设定为 static 不能进行 new 来创建private SingletonLazy(){}
}

到此,关于懒汉模式的相关问题基本解决。

相关文章:

JavaEE——单例模式

文章目录 一、介绍什么是单例模式二、饿汉模式三、懒汉模式四、讨论两种模式的线程安全问题 一、介绍什么是单例模式 在介绍单例模式之前,我们得先明确一个名词设计模式。 所谓设计模式其实不难理解,就是在计算机这个圈子中,呢些大佬们为了…...

关于数据倾斜

1、数据倾斜表现 1.1 hadoop中的数据倾斜表现 有一个多几个Reduce卡住,卡在99.99%,一直不能结束。各种container报错OOM异常的Reducer读写的数据量极大,至少远远超过其它正常的Reducer伴随着数据倾斜,会出现任务被kill等各种诡异…...

Shell第一次作业

要求: 1、判断当前磁盘剩余空间是否有20G,如果小于20G,则将报警邮件发送给管理员,每天检查一次磁盘剩余空间。 ​2、判断web服务是否运行(1、查看进程的方式判断该程序是否运行,2、通过查看端口的方式判断…...

实例解读nn.AdaptiveAvgPool2d((1, 1))

nn.AdaptiveAvgPool2d((1, 1))在PyTorch中创建一个AdaptiveAvgPool2d类的实例。该类在输入张量上执行2D自适应平均池化。 自适应平均池化是一种池化操作,它计算每个输入子区域的平均值并产生一个指定大小的输出张量。子区域的大小是根据输入张量的大小和输出张量的…...

泛型编程 之模板(template)

C另一种编程思想称为 泛型编程,主要利用的技术就是模板 目录 C另一种编程思想称为 泛型编程,主要利用的技术就是模板 一、概念 二、函数模板 1、语法与使用: 2、函数模板注意事项 3、普通函数与函数模板的区别 4、普通函数与函数模板的调用规…...

用ChatGPT问DotNet的相关问题,发现DotNet工程师的前景还不错

本人最近费了九牛二虎之力注册了一个ChatGPT账号,现在就给大家分享一下,问一下关于.NET的问题,看看ChatGPT的AI功能具体如何? 一、C#跟其它语言比较的优势 回答: C#是一门编程语言,它是为 Microsoft 的 …...

LeetCode_字符串_简单_415.字符串相加

目录 1.题目2.思路3.代码实现(Java) 1.题目 给定两个字符串形式的非负整数 num1 和num2,计算它们的和并同样以字符串形式返回。 你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将…...

Insix:面向真实的生成数据增强,用于Nuclei实例分割

文章目录 InsMix: Towards Realistic Generative Data Augmentation for Nuclei Instance Segmentation摘要本文方法数据增强方法具有形态学约束的前景增强提高鲁棒性的背景扰动 实验结果 InsMix: Towards Realistic Generative Data Augmentation for Nuclei Instance Segment…...

CleanMyMac X4.13.2最新版下载

现在cleanmymac x4.13.2中文版是大家首选的优秀mac清理软件。CleanMyMac集合了多种功能,几乎可以满足用户所有的清洁需求。它不仅包含各种清理功能,还具有卸载、维护、扩展、碎纸机等实用功能,可同时替代多种工具。它可以清理、优化、维护和监…...

机器学习算法原理:详细介绍各种机器学习算法的原理、优缺点和适用场景

目录 引言 二、线性回归 三、逻辑回归 四、支持向量机 五、决策树 六、随机森林 七、K-均值聚类 八、主成分分析(PCA) 九、K近邻算法 十、朴素贝叶斯分类器 十一、神经网络 十二、AdaBoost 十三、梯度提升树(Gradient Boosting T…...

Spring Security 6.0系列【32】授权服务器篇之默认过滤器

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.0.4 本系列Spring Security 版本 6.0.2 本系列Spring Authorization Server 版本 1.0.2 源码地址:https://gitee.com/pearl-organization/study-spring-security-demo 文章目录 前言1. OAuth2Authorizati…...

.NET中比肩System.Text.Json序列化反序列化组件MessagePack

简介 官方定义:MessagePack是一种高效的二进制序列化格式。它允许您像JSON一样在多个语言之间交换数据。但是它更快并且更小。 MessagePack是一种开源的序列化反序列化组件,可支持JAVA,C#等主流语言。在 C# 中使用 MessagePack&#xff0c…...

Oracle删除列操作:逻辑删除和物理删除

概念 逻辑删除:逻辑删除并不是真正的删除,而是将表中列所对应的状态字段(status)做修改操作,实际上并未删除目标列数据或恢复这些列占用的磁盘空间。比如0是未删除,1是删除。在逻辑上数据是被删除了&#…...

找出字符串中第一个匹配项的下标、求解方程----2023/5/2

找出字符串中第一个匹配项的下标、求解方程----2023/5/2 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1…...

23:宁以non-member、non-friend替换member函数

想象有个class用来表示网页浏览器。这样的class可能提供的众多函数中,有一些用来清除下载元素高速缓存区、清除访问过的URLs的历史记录、以及移除系统中的所有cookies: class WebBrowser{ public:void clearCache();void clearHistory();void removeCoo…...

Centos7安装Redis

一、安装gcc依赖 由于 redis 是用 C 语言开发,安装之前必先确认是否安装 gcc 环境(gcc -v),如果没有安装,执行以下命令进行安装 [rootlocalhost local]# yum install -y gcc 二、下载并解压安装包 [rootlocalhost l…...

Android 项目必备(四十五)-->2023 年如何构建 Android 应用程序

Android 是什么 Android 是一种基于 Linux 内核并由 Google 开发的开源操作系统。它用于各种设备包括智能手机、平板电脑、电视和智能手表。 目前,Android 是世界上移动设备使用最多的操作系统; 根据 statcounter 的一份最近 12 个月的样本报告;Android 的市场份额…...

改进YOLOv5: | 涨点神器 | 即插即用| ICLR 2022!Intel提出ODConv:即插即用的动态卷积

OMNI-DIMENSIONAL DYNAMIC CONVOLUTION ODConv实验核心代码ODConv代码yaml文件运行:论文链接: https://openreview.net/forum?id=DmpCfq6Mg39 本文介绍了一篇动态卷积的工作:ODConv,其通过并行策略采用多维注意力机制沿核空间的四个维度学习互补性注意力。作为一种“即插…...

( 数组和矩阵) 485. 最大连续 1 的个数 ——【Leetcode每日一题】

❓485. 最大连续 1 的个数 难度:简单 给定一个二进制数组 nums , 计算其中最大连续 1 的个数。 示例 1: 输入:nums [1,1,0,1,1,1] 输出:3 解释:开头的两位和最后的三位都是连续 1 ,所以最大…...

从0搭建Vue3组件库(十一): 集成项目的编程规范工具链(ESlint+Prettier+Stylelint)

欲先善其事,必先利其器。一个好的项目是必须要有一个统一的规范,比如代码规范,样式规范以及代码提交规范等。统一的代码规范旨在增强团队开发协作、提高代码质量和打造开发基石,所以每个人必须严格遵守。 本篇文章将引入 ESLintPrettierStylelint 来对代码规范化。 ESlint ES…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

LangFlow技术架构分析

🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 ​二、实现思路 总体思路: 用户通过Gradio界面上…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...