[設計模式] 懶漢式(雙重檢查加鎖版本)——爲什麼要有第二個if?uniqueInstance變量爲何要有volatile 修飾?

/**
 * 懶漢式(雙重檢查加鎖版本)
 * public class Singleton {
 *     private volatile static Singleton uniqueInstance;
 *     private Singleton() {}
 *     public static Singleton getInstance() {
 *         if (uniqueInstance == null) {
 *             synchronized (Singleton.class) {
 *                 if (uniqueInstance == null) {
 * 				//因爲如果沒有第二個if的話,在當前A線程獲得鎖的線程後可能有其他B線程也在等待進入這個Class鎖
 *                 //A線程創建鎖後創建實例,然後釋放鎖,之後等待池中的B線程獲得鎖,然後就會產生創建兩個線程的錯誤情況。
 *                     uniqueInstance = new Singleton();
 *                 }
 *             }
 *         }
 *         return uniqueInstance;
 *     }
 * }*/

爲什麼要有第二個if?

因爲如果沒有第二個if的話,在當前A線程獲得鎖的線程後可能有其他如B線程也在等待進入這個Class鎖,A線程獲取鎖後創建實例,然後釋放鎖,之後等待池中的B線程獲得鎖,然後就會產生創建兩個線程的錯誤情況。

uniqueInstance爲何要有volatile 修飾?

以下內容引用自https://www.cnblogs.com/tangZH/p/10031337.html

這個問題就涉及到了編譯原理,所謂編譯,就是把源代碼“翻譯”成目標代碼——大多數是指機器代碼——的過程。針對Java,它的目標代碼不是本地機器代碼,而是虛擬機代碼。編譯原理裏面有一個很重要的內容是編譯器優化。所謂編譯器優化是指,在不改變原來語義的情況下,通過調整語句順序,來讓程序運行的更快。這個過程成爲reorder。

JVM實現可以自由的進行編譯器優化。而我們創建變量的步驟:

1、申請一塊內存,調用構造方法進行初始化。

2、分配一個指針指向這塊內存。

而這兩個操作,JVM並沒有規定誰在前誰在後,那麼就存在這種情況:線程A開始創建SingletonClass的實例,此時線程B調用了getInstance()方法,首先判斷instance是否爲null。按照我們上面所說的內存模型,A已經把instance指向了那塊內存,只是還沒有調用構造方法,因此B檢測到instance不爲null,於是直接把instance返回了——問題出現了,儘管instance不爲null,但它並沒有構造完成,就像一套房子已經給了你鑰匙,但你並不能住進去,因爲裏面還沒有收拾。此時,如果B在A將instance構造完成之前就是用了這個實例,程序就會出現錯誤了。

在JDK 5之後,Java使用了新的內存模型。volatile關鍵字有了明確的語義——在JDK1.5之前,volatile是個關鍵字,但是並沒有明確的規定其用途——被volatile修飾的寫變量不能和之前的讀寫代碼調整,讀變量不能和之後的讀寫代碼調整!因此,只要我們簡單的把instance加上volatile關鍵字就可以了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章