当前位置:网站首页>硬核分析懒汉式单例
硬核分析懒汉式单例
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;
}
}
课外知识,有兴趣的可以研究一下指令重排序,本文不深入探讨,提供一张图供大家理解:
边栏推荐
- What is excess product power? Find the secret key of the second generation cs75plus in the year of the tiger
- 3年亏损136亿,上市能救活威马吗?
- Knowledge of affairs
- Installation and use of sonarqube
- [verification of SystemVerilog] ~ test platform, hardware design description, excitation generator, monitor and comparator
- 当开源遇见 KPI,全球化 VS 本土化,开源的理想与现实该如何和解?
- Lippon instrument software intern interview
- 理邦仪器软件实习生面试
- 在微服务架构中管理技术债务
- The reason why it is easy to let the single chip computer program fly
猜你喜欢

腾讯面试官分享面试经验,如何考察面试者技术及个人综合素质,给正在面试的你一点建议

Hebei huangjinzhai scenic spot adds "AED automatic defibrillator" to ensure the life safety of tourists!

Arthas实践操作文档记录

Raspberry pie obtains the function of network installation system without the help of other devices

河北 黄金寨景区新增“AED自动除颤器”保障游客生命安全!
[mysql_12] MySQL data types

思科瑞递交科创板注册:拟募资6亿 年营收2.22亿

How to play seek tiger, which has attracted much attention in the market?

【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()

02 _ 日志系统:一条SQL更新语句是如何执行的?
随机推荐
How about art plus online school? Is it a new online organization?
你还不懂线程池的设计及原理吗?掰开揉碎了教你设计线程池
How to batch insert 100000 pieces of data
Simple C language address book
英伟达研发主管:AI 是如何改进芯片设计的?
中国技术出海,TiDB 数据库海外探索之路 | 卓越技术团队访谈录
Summary of some classic embedded C interview questions
LoveLive! Published an AI paper: generating models to write music scores automatically
Backtracking / activity scheduling maximum compatible activities
【SystemVerilog 之 接口】~ Interface
Hashicopy之nomad应用编排方案04(伸缩及更新一个Job)
05 _ In simple terms index (Part 2)
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
Uniapp développe des applets Wechat, de la construction à la mise en ligne
Iclr2022| small sample fine tuning method of language model based on differentiable hints
Hashicopy之nomad应用编排方案03(运行一个job)
Analyse approfondie de la conception du système relationnel du Groupe de cercles
Exporting data using mysqldump
Nomad application layout scheme 04 of hashicopy (scaling and updating a job)
[multi thread performance tuning] what operations cause context switching?