1. 程式人生 > >java單例雙重檢查鎖為什麼需要加volatile關鍵字

java單例雙重檢查鎖為什麼需要加volatile關鍵字

Re: 炸斯特 2015-09-04 10:49發表 [回覆] [引用] [舉報]


回覆qq_30486849:我的理解,volatile是要保證可見性,即instance例項化後馬上對其他執行緒可見,而synchronized能同時保證原子性和可見性,同一時刻只有一個執行緒獲取鎖然後執行同步程式碼,並且在釋放鎖之前會將對變數的修改重新整理到主存當中,保證當其他執行緒再進入的時候,在主存中讀取到的就是最新的變數內容了。所以只要synchronized就夠了。
Re: AC是男孩 3分鐘前發表 [回覆]


回覆炸斯特:今天面試被問到這個問題,直接懵逼,在這裡找到答案,謝謝。
Re: 炸斯特 2015-09-04 15:30發表 [回覆] [引用] [舉報]


回覆炸斯特:剛剛想了想一些部落格裡面確實有提到一定要加volatile關鍵字,但是我的理解跟這篇部落格類似http://www.jianshu.com/p/977d27852826,所以感覺不用加volatile,這一部分回去看一下紙面資料,部落格內容終歸複製貼上的多,這種細節沒有考究,如果有別的觀點歡迎討論
Re: 炸斯特 2015-09-04 18:02發表 [回覆] [引用] [舉報]


回覆炸斯特:已經修改,的確應該加上volatile關鍵字。不加的情況下,假設兩個執行緒,執行緒A正在執行instance = new Instance()的操作,而執行緒B開始執行if(instance==null)的判斷,當不存在volatile的時候,因為 new Instance()是一個非原子操作,可能發生無序寫入,建構函式可能在整個物件構造完成前執行完畢,執行緒B可能會看到一個不完整的instance物件,因為java的某些實現會在記憶體中開闢一片儲存物件的區域後直接返回記憶體的引用,所以執行緒B判斷不為null,而這時候實際上,instance的建構函式還沒有執行,從而執行緒b得到不完整的物件。在 Instance 的建構函式執行之前,會在記憶體中開闢一片儲存物件的區域後直接返回記憶體的引用,賦值給變數 instance,instance也就可能成為非 null 的,即賦值語句在物件例項化之前呼叫,此時別的執行緒得到的是一個還會初始化的物件,這樣會導致系統崩潰執行緒B可能會看到一個不完整的instance物件,因為java的某些實現,所以執行緒B判斷不為null。從而得到不完整的物件。
Re: 炸斯特 2015-12-26 15:47發表 [回覆] [引用] [舉報]


回覆炸斯特:今天再看這段話有些歧義。假設沒有關鍵字volatile的情況下,兩個執行緒A、B,都是第一次呼叫該單例方法,執行緒A先執行instance = new Instance(),該構造方法是一個非原子操作,編譯後生成多條位元組碼指令,由於JAVA的指令重排序,可能會先執行instance的賦值操作,該操作實際只是在記憶體中開闢一片儲存物件的區域後直接返回記憶體的引用,之後instance便不為空了,但是實際的初始化操作卻還沒有執行,如果就在此時執行緒B進入,就會看到一個不為空的但是不完整(沒有完成初始化)的Instance物件,所以需要加入volatile關鍵字,禁止指令重排序優化,從而安全的實現單例。
Re: 炫銘童話 昨天 15:52發表 [回覆]


回覆炸斯特:正解,建立物件可以分解為如下的3行虛擬碼memory=allocate(); //1:分配物件的記憶體空間ctorInstance(memory); //2:初始化物件instance=memory; //3:設定instance指向剛分配的記憶體地址上面3行程式碼中的2和3之間,可能會被重排序導致先3後2,
Re: June 2016-07-07 18:12發表 [回覆] [引用] [舉報]


回覆炸斯特:贊,這個解釋比上個解釋清晰,而且正確。