一个小的技术细节
在學習過之前的《單例》之后,相信大家一定對單例有了很深的理解,對于雙重校驗鎖的單例實現(xiàn)大家一定都不陌生。
不知道大家有沒有關注過一個細節(jié),那就是在雙重校驗鎖中的getInstance方法中,定義了一個局部變量來接收Singleton的單例對象。代碼實現(xiàn)如下:
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有關,我們知道,雙重校驗鎖單例為了避免發(fā)生指令重排,一定要使用volatile來定義單例對象。
其實如果大家對于volatile有深入理解的話,這個問題其實不難回答。為了保證共享變量在并發(fā)場景下的內存可見性,volatile變量的操作前后都會通過插入內存屏障來進行數(shù)據(jù)同步,即將線程的本地內存數(shù)據(jù)同步到主內存(或從主內存將數(shù)據(jù)同步到線程的本地內存)
而這個過程其實是有很大的損耗的,我們可以想辦法降低對于volatile變量的訪問次數(shù),那就是通過定義局部變量的方式。
因為局部變量并不是共享的,所以不需要進行線程本地內存和主存之間的數(shù)據(jù)同步,操作效率就會很高。
所以,使用局部變量,是一種性能提升的方式,可以減少主存與線程內存的拷貝次數(shù)。
有道無術,術可成;有術無道,止于術歡迎大家關注Java之道公眾號 好文章,我在看??總結
- 上一篇: Java 8中字符串拼接新姿势:Stri
- 下一篇: hdu 2159 FATE 二维