设计模式 五种不同的单例模式 懒汉式 饿汉式 枚举单例 容器化单例(Spring单例源码分析) 线程单例

单例模式

第一种 饿汉式

优点:执行效率高,性能高,没有任何的锁

缺点:某些情况下,可能会造成内存浪费

/*** @author LionLi*/
public class HungrySingleton {private static final HungrySingleton hungrySingleton = new HungrySingleton();private HungrySingleton(){}public static HungrySingleton getInstance(){return  hungrySingleton;}
}

测试用例与结果

/*** @author LionLi*/
public class Test {public static void main(String[] args){ExecutorService executor = Executors.newFixedThreadPool(5);PrintStream out = System.out;executor.execute(() -> out.println(HungrySingleton.getInstance()));executor.execute(() -> out.println(HungrySingleton.getInstance()));executor.execute(() -> out.println(HungrySingleton.getInstance()));executor.execute(() -> out.println(HungrySingleton.getInstance()));executor.execute(() -> out.println(HungrySingleton.getInstance()));executor.shutdown();}
}


多次运行符合要求不会出现问题

第二种 懒汉式

优点:节省了内存,线程安全
缺点:性能低

测试用例以下通用

/*** @author LionLi*/
public class Test {public static void main(String[] args){ExecutorService executor = Executors.newFixedThreadPool(5);PrintStream out = System.out;executor.execute(() -> out.println(LazySingletion.getInstance()));executor.execute(() -> out.println(LazySingletion.getInstance()));executor.execute(() -> out.println(LazySingletion.getInstance()));executor.execute(() -> out.println(LazySingletion.getInstance()));executor.execute(() -> out.println(LazySingletion.getInstance()));executor.shutdown();}
}

第一版本

/*** @author LionLi*/
public class LazySingletion {private static LazySingletion instance;private LazySingletion(){}public static LazySingletion getInstance(){if(instance == null){instance = new LazySingletion();}return instance;}
}

测试失败无法保证单例

第二版本 增加 synchronized 锁

/*** @author LionLi*/
public class LazySingletion {private static LazySingletion instance;private LazySingletion(){}public synchronized static LazySingletion getInstance(){if(instance == null){instance = new LazySingletion();}return instance;}
}


测试成功 可以保证单例 但性能较低 所有的线程全都被阻塞到方法外部排队处理

第三版本 细化锁 只锁创建方法提高性能

优点: 性能高了,线程安全了

缺点:可读性难度加大,不够优雅

此方法在各大开源框架源码内最为常见 又名双重校验单例

/*** @author LionLi*/
public class LazySingleton {/*** volatile 保证原子性具体用法百度*/private volatile static LazySingleton instance;private LazySingleton(){}public static LazySingleton getInstance(){// 检查实例是否已经初始化 如果已经初始化直接返回 避免进入锁提高性能if (instance == null) {synchronized (LazySingleton.class) {// 重新检查是否已经被其他线程初始化if (instance == null) {instance = new LazySingleton();}}}return instance;}
}



测试成功 可以保证单例 性能还高 可以避免不必要的加锁

第三种 枚举单例

在这种实现方式中,既可以避免多线程同步问题,还可以防止通过反射和反序列化来重新创建新的对象。

Java虚拟机会保证枚举对象的唯一性,因此每一个枚举类型和定义的枚举变量在JVM中都是唯一的。

/*** @author LionLi*/
public enum EnumSingleton {INSTANCE;private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}
}

测试代码与结果

/*** @author LionLi*/
public class Test {public static void main(String[] args){ExecutorService executor = Executors.newFixedThreadPool(5);PrintStream out = System.out;// 设置一个对象便于查看内存地址EnumSingleton.INSTANCE.setData(new Object());executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));executor.shutdown();}
}



测试成功 可以保证单例 代码简单非常优雅

第四种 Spring中的单例模式实现 也可以称为 容器化单例

大家可以通过idea搜索找到 Spring 源码中的 DefaultSingletonBeanRegistrygetSingleton 方法 查看 Spring 是如何编写的

这里涉及到三个单例容器:

  • singletonObjects
  • earlySingletonObjects
  • singletonFactories

单例的获取顺序是singletonObjects -> earlySingletonObjects -> singletonFactories 这样的三级缓存

我们发现,在 singletonObjects 中获取 bean的时候,没有使用 synchronized 关键字

而在 singletonFactoriesearlySingletonObjects 中的操作都是在 synchronized 代码块中完成的,正好和他们各自的数据类型对应

singletonObjects 使用的使用 ConcurrentHashMap 线程安全,而 singletonFactoriesearlySingletonObjects 使用的是 HashMap 线程不安全。

从字面意思来说:singletonObjects 指单例对象的缓存,singletonFactories 指单例对象工厂的缓存,earlySingletonObjects 指提前曝光的单例对象的缓存。

以上三个构成了三级缓存,Spring 就用这三级缓存巧妙的解决了循环依赖问题。

除了这三个缓存之外 最核心的就是上面讲到的 双重校验单例 写法

第五种 特殊单例 线程单例

顾名思义 保证在所有线程内的单例

常见使用场景 日志框架 确保每个线程内都有一个单例日志实例 保证日志记录和输出的唯一性

提到线程 我们肯定会想到 在线程内最常使用的东西 那就是 TheadLocal 他可以保证线程之间的变量隔离 我们就基于他来实现线程单例

public class ThreadLocalSingleton {// 通过 ThreadLocal 的初始化方法 withInitial 初始化对象实例 保证线程唯一private static final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance =ThreadLocal.withInitial(() -> new ThreadLocalSingleton());private ThreadLocalSingleton(){}public static ThreadLocalSingleton getInstance(){return threadLocaLInstance.get();}
}

测试用例与运行结果

/*** @author LionLi*/
public class Test {public static void main(String[] args){ExecutorService executor = Executors.newFixedThreadPool(5);PrintStream out = System.out;executor.execute(() -> {out.println(ThreadLocalSingleton.getInstance());out.println(ThreadLocalSingleton.getInstance());});executor.execute(() -> {out.println(ThreadLocalSingleton.getInstance());out.println(ThreadLocalSingleton.getInstance());});executor.shutdown();}
}

测试符合预期 不同线程下的实例是单例的

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

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

相关文章

英文论文降重修改技巧 papergpt

大家好&#xff0c;今天来聊聊英文论文降重修改技巧&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 英文论文降重修改技巧 作为网站编辑&#xff0c;我们经常需要处理大量…

冒泡排序法

1.数组排序 题目描述 对数组的元素按从小到大进行排序。输入有两行 第一行有一个整数n( 5 < n < 10 ) 第二行有n个整数输出输出更新后的数组 样例 输入复制 8 1 2 3 6 8 7 4 5 输出复制 1 2 3 4 5 6 7 8 #include<iostream> using namespace std; int main(…

遥感图像之多模态检索AMFMN(支持关键词、句子对图像的检索)论文阅读、环境搭建、模型测试、模型训练

一、论文阅读 1、摘要背景 遥感跨模态文本图像检索以其灵活的输入和高效的查询等优点受到了广泛的关注。然而&#xff0c;传统的方法忽略了遥感图像多尺度和目标冗余的特点&#xff0c;导致检索精度下降。为了解决遥感多模态检索任务中的多尺度稀缺性和目标冗余问题&#xff…

CSS3 2D变形 过渡 动画

​​​​​ transform(2D变形)概述translate()平移scale()缩放skew()倾斜rotate()旋转transform-origin中心原点 CSS3 2D变形 3D变形 过渡 动画 在CSS3中&#xff0c;动画效果包括4个部分&#xff1a;变形&#xff08;transform&#xff09;、3D变形、过渡&#xff08;transit…

PMP项目管理 - 采购管理

系列文章目录 PMP项目管理 - 质量管理 PMP项目管理 - 采购管理 PMP项目管理 - 资源管理 PMP项目管理 - 风险管理 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in…

专业面试刷题网站程序源码

介绍&#xff1a; 一个干净的面试刷题网站&#xff01;专业面试刷题网站&#xff0c;助你成为面试达人&#xff01;支持自由组卷、在线刷题、校招社招斩获大厂offer&#xff0c;求职必备! 用这个刷题代码&#xff0c;助你早日打进狼厂、鹅厂等各大厂&#xff0c;薪水直接等级…

【剪映】点滴剪时光

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

C# 如何控制多线程同步执行

写在前面 使用Task类来控制多线程的同步执行&#xff0c;可应用于多任务分发执行后&#xff0c;再做归并处理。Tas既拥有线程池的优点&#xff0c;同时也解决了使用ThreadPool不易控制的弊端&#xff1b;可以非常简便并可靠地实现多线程的顺序执行。 代码实现 public class …

王道考研--》单链表课后习题C语言代码实现(冲刺)

考研是许多计算机科学专业学生追求高学历、寻求更好就业前景的途径。在考研过程中&#xff0c;数据结构是一个非常重要的科目&#xff0c;而代码实现题更是其中的难点之一。在这篇文章中&#xff0c;我们将探讨如何通过实现数据结构代码问题来提升考研成绩。无论您是否有编程经…

硬件基础:光耦、可控硅、继电器、达林顿管、干簧管

光耦 光电耦合器&#xff08;optical coupler&#xff0c;英文缩写为OC&#xff09;亦称光电隔离器&#xff0c;简称光耦。 光电耦合器是一种把发光器件和光敏器件封装在同一壳体内&#xff0c; 中间通过电→光→电的转换来传输电信号的半导体光电子器件。其中&#xff0c;发光…

re:Invent2023大会隆重推出自研芯片Graviton4和Trainium2

目录 一、前言 二、体验Graviton系列产品 &#xff08;一&#xff09;创建普通的EC2实例 &#xff08;二&#xff09;创建Graviton处理器的EC2实例 &#xff08;三&#xff09;远程到服务器 方式1&#xff1a;创建成功时连接 方式2&#xff1a;SSH客户端 方式3&#xff1a;正确…

基于FPGA的视频接口之高速IO(SATA)

简介 本章节是对于高速IO接口应用的一个扩展,目前扩展为SATA(SSD硬盘,机械硬盘不能使用)。通俗易懂的讲,即把SSD硬盘当做大型的Nand Flash来处理,不格式化硬盘,直接以地址和数据的格式,在SATA盘中写入数据,该数据不能被Window和linux直接识别,需单独编写App来查看SSD…

创建型模式之工厂模式

​ 本质&#xff1a; 实例化对象不直接使用new&#xff0c;而是用工厂代替 工厂模式分为&#xff1a; 简单工厂模式&#xff1a;用来生产同一等级结构中的任意产品&#xff08;增加新产品需要修改已有代码&#xff09;工厂方法模式&#xff1a;用来生产同一等级结构中的固定产…

关于找不到XINPUT1_3.dll,无法继续执行代码问题的5种不同解决方法

一、xinput1_3.dll的作用 xinput1_3.dll是Windows操作系统中的一款动态链接库文件&#xff0c;主要用于支持游戏手柄和游戏输入设备。这款文件属于Microsoft Xbox 360兼容性库&#xff0c;它包含了与游戏手柄和其他输入设备相关的功能。在游戏中&#xff0c;xinput1_3.dll负责…

C语言——预处理详解(#define用法+注意事项)

#define 语法规定 #define定义标识符 语法: #define name stuff #define例子 #include<stdio.h> #define A 100 #define STR "abc" #define FOR for(;;)int main() {printf("%d\n", A);printf("%s\n", STR);FOR;return 0; } 运行结果…

JRT实现在线打印预览

在JRT打印元素绘制协议一篇已经介绍过打印把绘图和打印逻辑进行了分离&#xff0c;这是和老设计最大的不同。因为老的设计时候没想着做在线预览功能&#xff0c;是后面硬性扩出来的。这次从最初设计就考虑绘图逻辑各处共用&#xff0c;包括打印预览&#xff0c;在线打印预览等、…

JS代码输出题:return Promise.resolve() 情况

题目&#xff1a; Promise.resolve().then(() > {console.log(0);return Promise.resolve(4);}).then((res) > {console.log(res)})Promise.resolve().then(() > {console.log(1)}).then(() > {console.log(2)}).then(() > {console.log(3)}).then(() > {con…

MDK编译过程和文件类型

MDK是一款IDE软件&#xff0c;具有&#xff0c;编辑&#xff0c;编译&#xff0c;链接&#xff0c;下载&#xff0c;调试等等的功能。 1.编译器介绍&#xff1a; MDK可以编译C/C文件和汇编文件&#xff0c;MDK只是一款IDE软件&#xff0c;那他内部使用的是什么编译器呢&#x…

Python-折线图可视化

折线图可视化 1.JSON数据格式2.pyecharts模块介绍3.pyecharts快速入门4.创建折线图 1.JSON数据格式 1.1什么是JSON JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据JSON本质上是一个带有特定格式的字符串 1.2主要功能json就是一种在各个编程语言中流…

JavaSE第7篇:封装

文章目录 一、封装1、好处:2、使用 二、四种权限修饰符三、构造器1、作用2、说明3、属性赋值的过程 一、封装 封装就是将类的属性私有化,提供公有的方法访问私有属性 不对外暴露打的私有的方法 单例模式 1、好处: 1.只能通过规定的方法来访问数据 2.隐藏类的实例细节,方便…