在学习过之前的《单例》之后,相信大家一定对单例有了很深的理解,对于双重校验锁的单例实现大家一定都不陌生。
不知道大家有没有关注过一个细节,那就是在双重校验锁中的getInstance方法中,定义了一个局部变量来接收Singleton的单例对象。代码实现如下:
public
class
Singleton
{
private
static volatile Singleton instance
=
null
;
private
Singleton
(
)
{
}
public
static Singleton
getInstance
(
)
{
Singleton temp
=instance
;
// 定义了一个局部变量
if
(
null
== temp
)
{
//对局部变量进行非空判断
synchronized
(
Singleton
.class
)
{
temp
= instance
;
if
(
null
== temp
)
{
temp
=
new
Singleton
(
)
;
//对局部变量进行赋值
instance
=temp
;
//再将局部变量赋值给单例对象
}
}
}
return instance
;
//返回单例对象
}
}
以上,便是一个双重校验锁的代码,可以看到,在getInstance方法中定义了一个局部变量temp,在操作过程中都是对这个临时的局部变量进行的操作,最后再赋值给真正的单例对象的。
在很多源码中,也都有类似的做法,如Spring中有以下代码:
private
static volatile ReactiveAdapterRegistry sharedInstance
;
public
static ReactiveAdapterRegistry
getSharedInstance
(
)
{
//vx耗:mbz_java_panlong 十年开发经验程序员,免费解答,备注“MI”即可
ReactiveAdapterRegistry registry
= sharedInstance
;
if
(registry
==
null
)
{
synchronized
(
ReactiveAdapterRegistry
.class
)
{
registry
= sharedInstance
;
if
(registry
==
null
)
{
registry
=
new
ReactiveAdapterRegistry
(
)
;
sharedInstance
= registry
;
}
}
}
return registry
;
}
那么,你知道为什么要这么做吗?
这里其实和volatile有关,我们知道,双重校验锁单例为了避免发生指令重排,一定要使用volatile来定义单例对象。
其实如果大家对于volatile有深入理解的话,这个问题其实不难回答。为了保证共享变量在并发场景下的内存可见性,volatile变量的操作前后都会通过插入内存屏障来进行数据同步,即将线程的本地内存数据同步到主内存(或从主内存将数据同步到线程的本地内存)
而这个过程其实是有很大的损耗的,我们可以想办法降低对于volatile变量的访问次数,那就是通过定义局部变量的方式。
因为局部变量并不是共享的,所以不需要进行线程本地内存和主存之间的数据同步,操作效率就会很高。
所以,使用局部变量,是一种性能提升的方式,可以减少主存与线程内存的拷贝次数。
(全文完)
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70010294/viewspace-2847955/ ,如需转载,请注明出处,否则将追究法律责任。