一個小的技術細節

在學習過之前的《單例》之後,相信大家一定對單例有了很深的理解,對於雙重校驗鎖的單例實現大家一定都不陌生。

不知道大家有沒有關注過一個細節,那就是在雙重校驗鎖中的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之道公衆號
好文章,我在看❤️
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章