天道不一定酬所有勤
但是,天道只酬勤

一个小的技术细节

GitHub 17k Star 的Java工程师成神之路,不来了解一下吗!

在学习过之前的《单例》之后,相信大家一定对单例有了很深的理解,对于双重校验锁的单例实现大家一定都不陌生。

不知道大家有没有关注过一个细节,那就是在双重校验锁中的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() {
    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变量的访问次数,那就是通过定义局部变量的方式。

因为局部变量并不是共享的,所以不需要进行线程本地内存和主存之间的数据同步,操作效率就会很高。

所以,使用局部变量,是一种性能提升的方式,可以减少主存与线程内存的拷贝次数。

赞(0)
如未加特殊说明,此网站文章均为原创,转载必须注明出处。HollisChuang's Blog » 一个小的技术细节
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

HollisChuang's Blog

联系我关于我