一个小的技术细节

一个小的技术细节_第1张图片

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

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

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

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

有道无术,术可成;有术无道,止于术欢迎大家关注Java之道公众号
好文章,我在看❤️

你可能感兴趣的:(一个小的技术细节)