设计模式 五种不同的单例模式 懒汉式 饿汉式 枚举单例 容器化单例(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,一经查实,立即删除!

相关文章

12 Vue3中的监听器

概述 Vue watchers programmatically observe component data and run whenever a particular property changes. Watched data can contain two arguments: oldVal and newVal. This can help you when writing expressions to compare data before writing or binding new v…

英文论文降重修改技巧 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…

linux 阻塞io

睡眠的介绍 对于一个进程"睡眠"意味着什么? 当一个进程被置为睡眠, 它被标识为处于一个特殊的状 态并且从调度器的运行队列中去除. 直到发生某些事情改变了那个状态, 这个进程将不被 在任何 CPU 上调度, 并且, 因此, 将不会运行. 一个睡着的进程已被搁置到系统的一…

Python中的并发编程是什么,如何使用Python进行并发编程?

并发编程是指在一个程序中同时运行多个任务的能力。在Python中&#xff0c;可以使用threading模块进行并发编程。以下是一个简单的例子&#xff1a; import threadingdef print_numbers():for i in range(10):print(i)def print_letters():for letter in abcdefghij:print(let…

CSS3 2D变形 过渡 动画

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

NX二次开发(NXOpenC++) - 切换制图模块方法

一、概述 在NX二次开发过程中&#xff0c;在某些应用场景中,经常要在建模环境和制图环境间的相互切换&#xff0c;NX切换至制图模块的方法有多种&#xff0c;不同的NX版本有所不同&#xff0c;有的是同一种方法支持多个版本&#xff0c;本技巧将列举切换至制图模块的各种方法。…

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 的首页,持续学…

敏捷开发-任务拆解、工作量评估和任务指派

在之前的文章我首先讲了1)敏捷的第一步-每日站立会&#xff0c;然后讲了如何2)用看板管理项目或者管理自己的工作待办&#xff0c;今天是第三个主题&#xff0c;讲如何3)在实际项目中做任务拆解、估时和工作指派。 任务拆解和评估 任务拆解和评估是一项需要非常细致、需要经验…

bin后缀的固件怎么解包和封包

bin后缀的固件解包和封包可以通过以下步骤进行&#xff1a; 解包&#xff1a; 使用解包工具&#xff1a;有许多解包工具可用于处理bin文件&#xff0c;例如UltralISO等。这些工具可以将bin文件解包为单个文件。选择目标文件夹&#xff1a;在解包之前&#xff0c;选择一个目标…

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

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

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

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

设计模式模板方法模式讲解和代码示例

引言 模版方法是一种行为设计模式, 它在基类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 Java语言 使用示例: 模版方法模式在 Java 框架中很常见。 开发者通常使用它来向框架用户提供通过继承实现的、 对标准功能进行扩展的简单方式。 这…

K8S(十)—容器探针

这里写目录标题 容器探针&#xff08;probe&#xff09;检查机制探测结果探测类型何时该使用存活态探针?何时该使用就绪态探针?何时该使用启动探针&#xff1f; 使用exechttptcpgrpc使用命名端口 使用启动探针保护慢启动容器定义就绪探针配置探针HTTP 探测TCP 探测探针层面的…

JavaScript中的变量提升:解析、应用及示例

JavaScript中的变量提升是一个常见但容易令人困惑的概念。在本文中&#xff0c;我们将深入探讨JavaScript中的变量提升&#xff0c;包括其含义、用途以及通过代码示例进行解释。 什么是变量提升&#xff1f; 在JavaScript中&#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;正确…