当前位置:网站首页>硬核分析懒汉式单例
硬核分析懒汉式单例
2022-06-11 15:18:00 【Evader1997】
本文主要围绕懒汉式单例模式展开,主要是关于线程安全。
看过我单例模式的下伙伴都知道,实际单例模式的定义很简单,就是有个私有的无参构造,以及对外提供一个获取单例的方法。懒汉式最简单的方式是这样的:
public class LazySingeton {
private static LazySingeton singeton;
private LazySingeton() {
}
public LazySingeton getSingeton() {
if (singeton == null) {
singeton = new LazySingeton();
}
return singeton;
}
}
这个代码在单线程情况下是没有问题的,但是在多线程环境下就需要考虑线程安全的问题。举个不恰当的例子,但是印象肯定深刻,话说男生宿舍卫生间就一个坑位,A进卫生间时没有锁门,这个时候B肚子疼直接推门而进,立即脱下裤子蹲下。后面发生的就自行脑补了,总之这肯定是有问题的,A还没用完坑位,B就进来了,这不是强人所男,男上加男吗?迎男而上这种事发生的概率还是比较小的。所以解决这个问题的方法就是A进卫生间后把卫生间门锁上。
巧了,Java中正好有锁,先加上锁,就像这样:
public class LazySingeton {
private static LazySingeton singeton;
private LazySingeton() {
}
// 添加synchronized锁,保证获取单例的方法只被一个地方调用
public synchronized LazySingeton getSingeton() {
if (singeton == null) {
singeton = new LazySingeton();
}
return singeton;
}
}
通过synchronized关键字可以保证线程安全问题,但是当前这种写法是可优化的,具体怎么优化呢?还是上卫生间这个例子,有些卫生间的门是带提示功能的,关门显示有人,开门显示没人。这钟门大家伙一看就知道里面是否有人,如果是一个不带提示功能的门,正常操作都是敲敲门,问问有人没。
上述代码就是敲门的操作,这是比较消耗性能的,每一个请求过来都需要判断getSingeton()方法是否加锁,相当于有个哥们正在坑位上蹲着,没进来一个哥们敲一下门,嗨,里面有人吗?
所以我们应该加一个提示功能,看看卫生间是否有没有人?对应到代码里就是对单例singeton进行判断,是否为null。如果为null就不再初始化(不问坑位上有没有人了),直接返回(直接走了)。这种方式是不是非常有效率呢,代码如下:
public class LazySingeton {
private static LazySingeton singeton;
private LazySingeton() {
}
public LazySingeton getSingeton() {
// 判断单例是否为null,不为null直接返回即可(减少每次判断是否加锁的性能消耗)
if (singeton == null) {
synchronized(LazySingeton.class){
singeton = new LazySingeton();
}
}
return singeton;
}
}
看完这版代码后脑洞比较大的同学会有这个疑问,这样不是还是需要判断是否加锁?思考的很对,这里**加锁在所难免,为了保证线程安全,敲门是肯定要敲的,但是减少敲门的次数是否等于性能提升了呢?**只有在singeton为null时才会去判断是否加锁,如果singeton不为null,那么就节省同步块中代码执行的消耗了。
到这里代码中还存在问题,这个问题看图会比较清晰:
出现这个问题的原因在于线程一还没new,线程二就通过了if(singeton = null)的判断,偷偷进入到后面的流程,这就好比一个人上卫生间还没来得及上锁,另一个人冲了进来。解决这个问题很简单,既然你跳过了if(singeton = null)的判断,那么我就在判断一次!,这样线程二即使侥幸逃过第一轮验证,也逃不过第二轮,从而保证只创建一个对象。具体代码如下:
public class LazySingeton {
private static LazySingeton singeton;
private LazySingeton() {
}
public LazySingeton getSingeton() {
// 第一次判断
if (singeton == null) {
synchronized (LazySingeton.class) {
// 第二次判断
if (singeton == null) {
singeton = new LazySingeton();
}
}
}
return singeton;
}
}
以上内容还有没有问题?答案是有的,接下来涉及到JVM相关知识:指令重排序。接下来的内容初学者大改能理解就行,第一次看的不必深挖。首先大家先看下图,看看singeton = new LazySingeton();这行代码在JVM是如何执行的。
这个流程大家应该都清楚,现在假设A,B两个线程同时来获取单例,A线程进入同步代码块,执行完上述操作后,直接释放锁。这个时候其他线程就会进入同步代码块,此时如果线程A只是刚在内存中开辟了空间,而没有实例化这个空壳子,那么第二个线程走到if时,就不会走if里面的代码。从而返回的是个null。文字有点不好理解,大家结合图片来看。
最终版代码如下:
public class LazySingeton {
// 增加volatile修饰符,防止指令重排序
private volatile static LazySingeton singeton;
private LazySingeton() {
}
public LazySingeton getSingeton() {
if (singeton == null) {
synchronized (LazySingeton.class) {
if (singeton == null) {
singeton = new LazySingeton();
}
}
}
return singeton;
}
}
课外知识,有兴趣的可以研究一下指令重排序,本文不深入探讨,提供一张图供大家理解:
边栏推荐
- Avenue to simplicity | how to configure self attention for vit is the most reasonable?
- 03 _ 事务隔离:为什么你改了我还看不见?
- Implementation of the function of recording login status
- 3年亏损136亿,上市能救活威马吗?
- How about art plus online school? Is it a new online organization?
- 05 _ In simple terms index (Part 2)
- Qcustomplot 1.0.1 learning (3) - plotting quadratic functions
- 深度解读:分布式系统韧性架构压舱石OpenChaos
- Nomad application layout scheme 04 of hashicopy (scaling and updating a job)
- Explain the kubernetes package management tool Helm
猜你喜欢

Qualcomm WLAN framework learning (29) -- 6GHz overview

Design and implementation of data analysis system

Arthas实践操作文档记录

Database optimization

04 _ In simple terms index (I)

见微知著,细节上雕花:SVG生成矢量格式网站图标(Favicon)探究

Station B executives interpret the financial report: the epidemic has no impact on the company's long-term development, and the video trend is irresistible

3年亏损136亿,上市能救活威马吗?

07 _ 行锁功过:怎么减少行锁对性能的影响?
![[SystemVerilog interface] ~ interface](/img/dc/0a9750cace1460af772e2f3f6a8763.png)
[SystemVerilog interface] ~ interface
随机推荐
06 _ 全局锁和表锁 :给表加个字段怎么有这么多阻碍?
Tencent interviewers share their interview experience, how to evaluate the interviewers' technical and personal comprehensive quality, and give you some suggestions on the interview
System. out. What should I pay attention to when using the println () method
C语言简易版webserver
Arthas practice documentation
Safepoint explanation and analysis of its placement ideas
Repository Manager之Nexus配置yum仓库
Recyclerview usage record
Hamad application layout scheme 03 of hashicopy (run a job)
Uniapp settings page Jump effect - navigateto switching effect - Global animationtype animation
China's technology goes to sea, tidb database's overseas exploration road | interview with excellent technical team
Lippon instrument software intern interview
2021 年 CNCF 调查:Kubernetes 跨越鸿沟的一年
基于STM32F1的开源小项目
02 Tekton Pipeline
[verification of SystemVerilog] ~ test platform, hardware design description, excitation generator, monitor and comparator
04 _ 深入浅出索引(上)
Avenue to simplicity | how to configure self attention for vit is the most reasonable?
Tangzhengrong: CTO is the intersection of business thinking and technical thinking
Uniapp développe des applets Wechat, de la construction à la mise en ligne