二狗:二胖你昨天請假了是否是又去面試了啊?
二胖:別說了我就出去試試水,看看如今工做好很差找,順帶出去找找打擊,而後才能好好靜下心來好好學習。
二狗: 那被打擊的怎麼樣啊?知道本身是什麼樣的水平了吧,壞笑。
二胖:基礎太差,一面就讓回去等通知了,我要好好學習了,不跟你瞎扯了。
二狗: 都問了你什麼問題啊,把你打擊成這樣?一塊兒覆盤下讓我也好好準備下啊。
二胖:好吧,你既然這麼好奇,那我就大概說下吧,你搬上小板凳仔細挺好了哦。我要開始個人表演了。
下面二胖第一面開始了。
面試官:二胖是吧,先作個自我介紹吧。
二胖:好的,我叫二胖,我來自長沙,今年25歲,從事java開發快3年了,如今在XX公司XX事業部擔任高級java開發工程師,主要負責XX系統。。。。。
面試官:好的,我看你簡歷上寫着熟練掌握併發編程你能跟我說說併發編程裏面你都知道哪些關鍵字。
二胖: 這不就是要考我 synchronized
和volatile
這個我擅長啊,我特地背過的,synchronized
是java
提供的一個關鍵字它主要能保證原子性、有序性它的底層主要是經過Monitor
來實現的。volatile
也是java的一個關鍵字它的主要做用是能夠保證可見性。。。。此處省略1000字。
面試官:八股文背的不錯,說了這麼多,咱們來動手試試吧,寫一個雙重校驗鎖(dcl)的單例我看看。
二胖: 從屁股口袋裏拿出了筆三下五除二就把它默寫出來了。
面試官:你有說道volatile
關鍵字和synchronized
關鍵字。synchronized
能夠保證原子性、有序性和可見性。而volatile
卻只能保證有序性和可見性。那麼,咱們再來看一下雙重校驗鎖實現的單例,已經使用了synchronized
,爲何還須要volatile
?這個volatile
是否能夠去掉?
二胖: 讓我想一想,貌似好像確實能夠去掉。
面試官: 咱們今天的面試就到這裏吧,後續有消息人事會聯繫你,感謝你今天來面試。java
二胖很鬱悶回去谷歌了下這個問題,stackoverflow上也有這個問題,看樣子不僅我一我的不知道這個問題嗎?看樣子面試掛的不冤
以上故事純屬虛構,若有雷同請以本文爲主。面試
咱們先來看看沒有加volatile 修飾的單例:編程
1 public class Singleton { 2 private static Singleton singleton; 3 private Singleton (){} 4 public static Singleton getSingleton() { 5 if (singleton == null) { 6 synchronized (Singleton.class) { 7 if (singleton == null) { 8 singleton = new Singleton(); 9 } 10 } 11 } 12 return singleton; 13 } 14 }
上述代碼看下來是否是感受沒啥問題。
首先咱們先來看下這一行代碼到底幹了哪些事情併發
singleton = new Singleton()
上述過程咱們能夠簡化成3個步驟:ide
ThreadA
和ThreadB
同時來請求Singleton.getSingleton
方法:ThreadA
進入到第8行,執行 singleton = new Singleton()
進行對象的初始化(按照對象初始化的過程 ①②③)執行完。ThreadB
進入第5
行判斷singleton
不爲空(第一步已經初始化好了),直接返回singleton
ThreadA
進入到第8行,執行 singleton = new Singleton()
執行完.①JVM
爲對象分配一塊內存M
。③將內存的地址複製給singleton
變量。ThreadB
直接進入第5行,發現singleton
已經不爲空了而後直接就跳轉到12
行拿到這個singleton
返回去執行操做去了。此時ThreadB
拿到的singleton
對象是個半成品對象,由於尚未爲這個對象進行初始化(②還沒執行)。ThreadB
拿到的對象去執行方法可能會有異常產生。至於爲何會這樣列?《Java 併發編程實戰》有提到
有 synchronized 無 volatile 的 DCL(雙重檢查鎖) 會出現的狀況:線程可能看到引用的當前值,但對象的狀態值確少失效的,這意味着線程能夠看到對象處於無效或錯誤的狀態。post
說白了也就是ThreadB是能夠拿到一個引用已經有了可是內存資源尚未分配的對象。
若是要解決建立對象按照①②③的順序,其實也就是爲了解決指令重排只要第2行加個volatile
修飾就好。
說好的synchronized 不是能夠保證有序性的嗎?volatile的有序性?synchronized 不能不夠保證指令重排嗎?
怎麼來定義順序呢?《深刻理解Java虛擬機第三版》有提到學習
Java程序中自然的有序性能夠總結爲一句話:若是在本線程內觀察,全部操做都是自然有序的。若是在一個線程中觀察另外一個線程,全部操做都是無序的。前半句是指「線程內似表現爲串行的語義」,後半句是指「指令重排」現象和「工做內存與主內存同步延遲」現象。優化
站在巨人的肩膀上摘蘋果:
https://stackoverflow.com/questions/7855700/why-is-volatile-used-in-double-checked-locking
https://juejin.cn/post/6844903920599302152線程