当前位置:网站首页>硬核分析懒汉式单例
硬核分析懒汉式单例
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;
}
}
课外知识,有兴趣的可以研究一下指令重排序,本文不深入探讨,提供一张图供大家理解:
边栏推荐
- 3年亏损136亿,上市能救活威马吗?
- 基于 GateWay 和 Nacos 实现微服务架构灰度发布方案
- Arthas practice documentation
- 在微服务架构中管理技术债务
- How can local retail release the "imprisoned value" and make physical stores grow again?
- Zhejiang University has developed a UAV, which can automatically avoid obstacles and walk through the woods like a bird. The real swarm is coming
- 数据库优化
- 回溯法/解空间树 排列树
- 07 _ 行锁功过:怎么减少行锁对性能的影响?
- 你还不懂线程池的设计及原理吗?掰开揉碎了教你设计线程池
猜你喜欢
![[multi thread performance tuning] what operations cause context switching?](/img/a6/5d82c81dba546092447debebf7fc3e.jpg)
[multi thread performance tuning] what operations cause context switching?

高数_第6章无穷级数__马克劳林级数

Hot seek tiger, a list of eco economic models

你还不懂线程池的设计及原理吗?掰开揉碎了教你设计线程池

03 _ 事务隔离:为什么你改了我还看不见?

06 _ 全局锁和表锁 :给表加个字段怎么有这么多阻碍?

Qcustomplot 1.0.1 learning (3) - plotting quadratic functions

Knowledge of affairs

C语言简易版webserver

Analysis on the architecture of distributed systems - transaction and isolation level (multi object, multi operation) Part 2
随机推荐
浅析分布式系统之体系结构-事务与隔离级别(多对象、多操作)下篇
基于 GateWay 和 Nacos 实现微服务架构灰度发布方案
深度剖析「圈组」关系系统设计 | 「圈组」技术系列文章
High number_ Chapter 6 infinite series__ Marklaurin series
Flutter 3.0 was officially released: it stably supports 6 platforms, and byte jitter is the main user
[process blocks and methods of SystemVerilog] ~ domain, always process block, initial process block, function, task, life cycle
04 _ 深入浅出索引(上)
思科瑞递交科创板注册:拟募资6亿 年营收2.22亿
Analysis on the architecture of distributed systems - transaction and isolation level (multi object, multi operation) Part 2
2021 年 CNCF 调查:Kubernetes 跨越鸿沟的一年
Backtracking / activity scheduling maximum compatible activities
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()
Riskscanner of multi Cloud Security compliance scanning platform
Nexus configuration Yum repository for repository manager
In depth analysis of "circle group" relationship system design | series of articles on "circle group" technology
Managing technology debt in a microservice architecture
回溯法/活动安排 最大兼容活动
Hebei huangjinzhai scenic spot adds "AED automatic defibrillator" to ensure the life safety of tourists!
Can we really make money by doing we media editing?
Architectural concept exploration: Taking the development of card games as an example