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

07 创建型模式-单例模式

1.单例模式介绍

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,此模式保证
某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类。

2.使用单例模式要做的两件事
  1. 保证一个类只有一个实例
  2. 为该实例提供一个全局访问节点
3. 单例模式结构

在这里插入图片描述

4.1 单例模式之饿汉式:

在类加载期间初始化静态实例,保证 instance 实例的创建是线程安全的 ( 实例在
类加载时实例化,有JVM保证线程安全).
特点: 不支持延迟加载实例(懒加载) , 此中方式类加载比较慢,但是获取实例对象
比较快。

问题: 该对象足够大的话,而一直没有使用就会造成内存的浪费。

public class Singleton_01 {//1. 私有构造方法private Singleton_01(){}//2. 在本类中创建私有静态的全局对象private static Singleton_01 instance = new Singleton_01();//3. 提供一个全局访问点,供外部获取单例对象public static Singleton_01 getInstance(){return instance;}}
4.2懒汉式(线程不安全)

此种方式的单例实现了懒加载,只有调用getInstance方法时 才创建对象.但是如
果是多线程情况,会出现线程安全问题.

public class Singleton_02 {//1. 私有构造方法private Singleton_02(){}//2. 在本类中创建私有静态的全局对象private static Singleton_02 instance;//3. 通过判断对象是否被初始化,来选择是否创建对象public static Singleton_02 getInstance(){if(instance == null){instance = new Singleton_02();}return instance;}}

注意:
假设在单例类被实例化之前,有两个线程同时在获取单例对象,线程A在执
行完if (instance == null) 后,线程调度机制将 CPU 资源分配给线程B,此
时线程B在执行 if (instance == null)时也发现单例类还没有被实例化,这样
就会导致单例类被实例化两次。为了防止这种情况发生,需要对
getInstance() 方法同步处理。改进后的懒汉模式.

懒汉式(线程安全)

原理: 使用同步锁 synchronized 锁住 创建单例的方法 ,防止多个线程同时调
用,从而避免造成单例被多次创建。

public class Singleton_03 {//1. 私有构造方法private Singleton_03(){}//2. 在本类中创建私有静态的全局对象private static Singleton_03 instance;//3. 通过添加synchronize,保证多线程模式下的单例对象的唯一性public static synchronized Singleton_03 getInstance(){if(instance == null){instance = new Singleton_03();}return instance;}}

缺点
懒汉式的缺点也很明显,我们给 getInstance() 这个方法加了一把大锁
(synchronzed),导致这个函数的并发度很低。量化一下的话,并发度
是 1,也就相当于串行操作了。而这个函数是在单例使用期间,一直会被调
用。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,
如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶
颈,这种实现方式就不可取了。

双重校验

饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。那我们再来看一
种既支持延迟加载、又支持高并发的单例实现方式,也就是双重检测实现方
式。

实现步骤:

  1. 在声明变量时使用了 volatile 关键字,其作用有两个:
    保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时
    候,其他线程可以立刻得到修改之后的结果。
    屏蔽指令重排序:指令重排序是编译器和处理器为了高效对程序进行优化的手
    段,它只能保证程序执行的结果时正确的,但 是无法保证程序的操作顺序与代
    码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题。
  2. 将同步方法改为同步代码块. 在同步代码块中使用二次检查,以保证其不被
    重复实例化 同时在调用getInstance()方法时不进行同步锁,效率高。
public class Singleton_04 {//1. 私有构造方法private Singleton_04(){}//2. 使用 volatile保证变量的可见性private volatile static Singleton_04 instance = null;//3. 对外提供静态方法获取对象public static Singleton_04 getInstance(){//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例if(instance == null){synchronized (Singleton_04.class){//抢到锁之后再次进行判断是否为nullif(instance == null){instance = new Singleton_04();}}}return instance;}}
静态内部类

原理 根据静态内部类 的特性(外部类的加载不影响内部类),同时解决了按
需加载、线程安全的问题,同时实现简洁。

  1. 在静态内部类里创建单例,在装载该内部类时才会去创建单例
  2. 线程安全:类是由 JVM 加载,而 JVM 只会加载1遍,保证只有1个单例
public class Singleton_05 {private static class SingletonHandler{private static Singleton_05 instance = new Singleton_05();}private Singleton_05(){}public static Singleton_05 getInstance(){return SingletonHandler.instance;}}
反射对于单例的破坏

反射技术过于强大,它可以通过 setAccessible() 来修改构造器,字段,方法
的可见性。单例模式的构造方法是私有的,如果将其可见性设为 public ,那么
将无法控制对象的创建。

public class Test_Reflect {public static void main(String[] args) {try {//反射中,欲获取一个类或者调用某个类的方法,首先要获取到该类的Class 对象。Class<Singleton_05> clazz = Singleton_05.class;//getDeclaredXxx: 不受权限控制的获取类的成员.Constructor c = clazz.getDeclaredConstructor(null);//设置为true,就可以对类中的私有成员进行操作了c.setAccessible(true);Object instance1 = c.newInstance();Object instance2 = c.newInstance();System.out.println(instance1 == instance2);} catch (Exception e) {e.printStackTrace();}}}

解决方法之一: 在单例类的构造方法中 添加判断 instance != null 时,直接抛
出异常

public class Singleton_05 {private static class SingletonHandler{private static Singleton_05 instance = new Singleton_05();}private Singleton_05(){if(SingletonHandler.instance != null){throw new RuntimeException("不允许非法访问!");}}public static Singleton_05 getInstance(){return SingletonHandler.instance;}
}

上面的这种方式使代码简洁性遭到破坏,设计不够优雅.

序列化对于单例的破坏
public class Test_Serializable {@Testpublic void test() throws Exception{//序列化对象输出流ObjectOutputStream oos = new ObjectOutputStream(newFileOutputStream("tempFile.obj"));oos.writeObject(Singleton.getInstance());//序列化对象输入流File file = new File("tempFile.obj");ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));Singleton Singleton = (Singleton) ois.readObject();System.out.println(Singleton);System.out.println(Singleton.getInstance());//判断是否是同一个对象System.out.println(Singleton.getInstance() == Singleton);//false}
}class Singleton implements Serializable{private volatile static Singleton singleton;private Singleton() {}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}

输出结构为false,说明:
通过对Singleton的序列化与反序列化得到的对象是一个新的对象,这就 破坏了
Singleton的单例性 。

解决方案:

/**
* 解决方案:只要在Singleton类中定义readResolve就可以解决该问题
* 程序会判断是否有readResolve方法,如果存在就在执行该方法,如果不存在-
-就创建一个对象
*/
private Object readResolve() {return singleton;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
readOrdinaryObject方法的代码片段

private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//此处省略部分代码
Object obj;
try {
//通过反射创建的这个obj对象,就是本方法要返回的对象,也
可以暂时理解为是ObjectInputStream的readObject返回的对象。
//isInstantiable:如果一个serializable的类可以在运行
时被实例化,那么该方法就返回true
//desc.newInstance:该方法通过反射的方式调用无参构造方
法新建一个对象。
obj = desc.isInstantiable() ? desc.newInstance()
: null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
return obj;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5 枚举(推荐方式)

在这里插入图片描述

public enum Singleton_06{INSTANCE;private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}public static Singleton_06 getInstance(){return INSTANCE;}
}

在这里插入图片描述
在这里插入图片描述

相关文章:

07 创建型模式-单例模式

1.单例模式介绍 单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一&#xff0c;此模式保证 某个类在运行期间&#xff0c;只有一个实例对外提供服务&#xff0c;而这个类被称为单例类。 2.使用单例模式要做的两件事 保证一个类只有一个实例…...

RunnerGo 支持UI自动化的测试平台

RunnerGo提供从API管理到API性能再到可视化的API自动化、UI自动化测试功能模块&#xff0c;覆盖了整个产品测试周期。 RunnerGo UI自动化基于Selenium浏览器自动化方案构建&#xff0c;内嵌高度可复用的测试脚本&#xff0c;测试团队无需复杂的代码编写即可开展低代码的自动化…...

LLM ReAct: 将推理和行为相结合的通用范式 学习记录

LLM ReAct 什么是ReAct? LLM ReAct 是一种将推理和行为相结合的通用范式,可以让大型语言模型(LLM)根据逻辑推理(Reason),构建完整系列行动(Act),从而达成期望目标。LLM ReAct 可以应用于多种语言和决策任务,例如问答、事实验证、交互式决策等,提高了 LLM 的效率、…...

聊聊分布式架构08——SpringBoot开启微服务时代

目录 微服务架构时代 快速入门 入门详解 SpringBoot的自动配置 石器时代&#xff1a;XML配置bean 青铜时代&#xff1a;SpringConfig 铁器时代&#xff1a;AutoConfigurationImportSelector 手写简单Starter SpringApplication启动原理 微服务架构时代 Spring Boot的…...

Vue项目中集成TinyMCE富文本编辑器(图片批量上传等)

TinyMCE富文本在Vue中的使用 关于TinyMCE 实现效果 安装使用TinyMCE 第一步 第二步 1.官网申请Your Tiny API Key&#xff0c;并且配置访问域名&#xff1a; 2.使用css隐藏(这个就不讲了&#xff0c;不推荐使用) 3.全部由本地加载(推荐) 第三步(汉化包) 第四步(封装组…...

前端数据可视化之【title、legend、tooltip、toolbox 】配置项

目录 &#x1f31f;Echarts配置项&#x1f31f;Echarts配置项之 title组件&#x1f31f;Echarts配置项之 legend组件&#x1f31f;Echarts配置项之 tooltip组件&#x1f31f;Echarts配置项之 toolbox组件&#x1f31f;写在最后 &#x1f31f;Echarts配置项 ECharts开源来自百度…...

microcom串口调试工具使用

microcom串口助手使用介绍 microcom是一个在终端中使用的串口助手&#xff0c;类似平常使用SSCOM一样的东西&#xff0c;不过是在终端中使用而已。 使用的是busybox构建的文件系统 microcom源码路径&#xff1a;busybox/miscutils/microcom.c microcom 参数&#xff1a; [r…...

深入了解Golang:基本语法与核心特性解析

1. 引言 Golang&#xff08;Go&#xff09;是谷歌开发的一门开源编程语言&#xff0c;于2007年首次公开亮相&#xff0c;随后在2012年正式发布。Golang以其简洁、高效和可靠的设计而备受开发者青睐。作为一门编译型语言&#xff0c;Golang具有静态类型和垃圾回收功能&#xff…...

短视频矩阵系统源码---php搭建

一、智能剪辑、矩阵分发、无人直播、爆款文案于一体独立应用开发 抖去推----主要针对本地生活的----移动端(小程序软件系统&#xff0c;目前是全国源头独立开发)&#xff0c;开发功能大拆解分享&#xff0c;功能大拆解&#xff1a; &#xff08;1&#xff09;数据概览&#x…...

mysql 查询表字段名,注释 , 以及sql拼接查询出的内容

#sql查询字段名&#xff0c;注释操作拼接 #查询字段名和注释 select COLUMN_NAME,COLUMN_COMMENT from information_schema.COLUMNS where table_name 表名 and table_schema 库名 order by ordinal_position #查询整个内容 select * from information_schema.COLUMNS wh…...

【JavaEE】_Servlet API

目录 1. HttpServlet 1.1 init方法 1.2 destroy方法 1.3 service方法 1.4 Servlet的生命周期 1.5 代码示例 1.5.1 使用postman构造请求 1.5.2 使用ajax构造请求 2. HttpServletRequest 2.1 核心方法 2.2 代码示例1&#xff1a;打印请求信息 3. 前端给后端传参 3.1…...

macOS下matplotlib如何显示中文字体?

一般要显示中文会使用&#xff1a; plt.rcParams[font.sans-serif][SimHei] #用来正常显示中文标签 plt.rcParams[axes.unicode_minus]False #用来正常显示负号不过在macOS下通常会显示方块字&#xff1a; 解决方案&#xff1a; 把上面两句注释掉&#xff0c;更换为&#xf…...

7-Zip怎么设置字典大小 单词大小 固实数据大小,把大文件9.35G压缩成小1.56G

环境: Win10 专业版 7-Zip v23.01 问题描述: 7-Zip怎么设置字典大小 单词大小 固实数据大小,把大文件9.28G压缩成小1.56G 解决方案: 要在7-Zip中设置字典大小、单词大小和固实数据大小,可以按照以下步骤进行操作: 打开7-Zip文件管理器,并导航到你要压缩的文件所在的…...

使用CPR库和Python编写程序

以下是一个使用CPR库和Python编写的爬虫程序&#xff0c;用于爬取。此程序使用了proxy的代码。 import requests from cpr import CPR ​ def get_proxy():url "https://www.duoip.cn/get_proxy"headers {"User-Agent": "Mozilla/5.0 (Windows NT …...

axios 请求的缓存封装

前言 咱们的网站或者程序&#xff0c;每一个页面和操作都需要请求后端接口来获取响应和渲染页面&#xff0c;抛开post请求方式的接口不说&#xff0c;部分get请求得到的数据&#xff0c;短时间内不会更新&#xff0c;或者短时间得到的响应数据不会变化&#xff0c;这个时候就可…...

Oracle性能调优实践中的几点心得

很多的时侯&#xff0c;做OracleDBA的我们&#xff0c;当应用管理员向我们通告现在应用很慢、数据库很慢的时侯&#xff0c;我们到数据库时做几个示例的Select也发现同样的问题时&#xff0c;有些时侯我们会无从下手&#xff0c;因为我们认为数据库的各种命种率都是满足Oracle文…...

勒索病毒最新变种.halo勒索病毒来袭,如何恢复受感染的数据?

引言&#xff1a; 在当今数字化时代&#xff0c;勒索病毒的威胁不断升级&#xff0c;其中.halo勒索病毒引起了广泛关注。本文91数据恢复将深入研究.halo勒索病毒的特点&#xff0c;探讨如何有效地恢复被其加密的数据文件&#xff0c;并分享预防这一威胁的关键措施。 .halo勒索…...

大厂秋招真题【前缀和】美团20230826秋招T5-平均数为k的最长连续子数组

文章目录 【前缀和】美团20230826秋招T5-平均数为k的最长连续子数组题目描述与示例题目描述输入描述输出描述示例输入输出说明 解题思路代码PythonJavaC时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 【前缀和】美团20230826秋招T5-平均数为k的最长连续子数组 题目描…...

bazel远程构建(Remote Execution) --- linux安装Redis

采用源码安装方式 下载地址&#xff1a;Download | Redis&#xff0c;下载最新稳定版本。 step1: 下载最新稳定版本 wget https://download.redis.io/redis-stable.tar.gz step2: 解压安装 tar -xzvf redis-stable.tar.gz cd redis-stable make 执行完 make 命令后&#…...

Maven在开发中的使用及理解

在JAVA项目中&#xff0c;我们通常需要对项目的构建和依赖进行管理&#xff0c;这个时候我们就需要MAVEN来对项目进行支持。 一.MAVEN构建 在整个MAVEN构建的过程中包含以下环节&#xff0c;也对应IDEA中MAVEN的对应功能。 清理Maven Clean 清理&#xff0c;则代表删除上一…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...