1. 程式人生 > >個人單子模式學習筆記(執行緒安全、雙重檢查、Volitale)

個人單子模式學習筆記(執行緒安全、雙重檢查、Volitale)

單子模式我已經栽了兩次了,第一次是單子模式沒有考慮執行緒安全,第二次被問到double check 和Volitale。鬼知道下一次我又栽在單子模式的哪兒,所以我決定,見到什麼問題,就都在這兒記著了。

我們先看這種雙重檢查,不加volatile

public static Singleton instance;
public static Singleton getInstance()
{
  if (instance == null)              //1
  {                                  //2
    synchronized(Singleton.class) {  //3
      if (instance == null)          //4
        instance = new Singleton();  //5
    }
  }
  return instance;
}

這種方式存在什麼問題呢?  也許有人說存在可見性問題:執行緒1執行完第5步,釋放鎖。執行緒2獲得鎖後執行到第4步,由於可見性的原因,發現instance還是null,從而初始化了兩次。  但是不會存在這種情況,因為synchronized能保證執行緒1在釋放鎖之前會講對變數的修改重新整理到主存當中,執行緒2拿到的值是最新的。

實際存在的問題是無序性。  第5步這個new操作是無序的,它可能會被編譯成:  - a. 先分配記憶體,讓instance指向這塊記憶體  - b. 在記憶體中建立物件

然而我們需要意識到這麼一個問題,synchronized雖然是互斥的,但不代表一次就把整個過程執行完,它在中間是可能釋放時間片的,時間片不是鎖。(我因為這裡沒轉過來,耽誤了很多時間)  也就是說可能在a執行完後,時間片被釋放,執行緒2執行到1,這時他讀到的instance是不是null呢?(標記1)  基於可見性,可能是null,也可能不是null。 非常奇葩的是

,在這個例子中,如果讀到的是null,反而沒問題了,接下來會等待鎖,然後再次判斷時不為null,最後返回單例。  如果讀到的不是null,那麼壞了,按邏輯它就直接return instance了,這個instance還沒執行構造引數,去做事情的話,很可能就崩潰了。

加volatile

public volatile static Singleton instance;
public static Singleton getInstance()
{
  if (instance == null)              //1
  {                                  //2
    synchronized(Singleton.class) {  //3
      if (instance == null)          //4
        instance = new Singleton();  //5
    }
  }
  return instance;
}

唯一的區別是加了volatile關鍵字,那麼會有什麼現象?  這時要區分jdk版本了,在jdk1.4及之前,volatile並不能保證new操作的有序性,但是它能保證可見性,因此標記1處,讀到的不是null,導致了問題。  從1.5開始,加了volatile關鍵字的引用,它的初始化就不能是:  - a. 先分配記憶體,讓instance指向這塊記憶體  - b. 在記憶體中建立物件

而應該是:  - a.在記憶體中建立物件  - b.讓instance指向這個物件.

這種形式,也就避免了無序性問題。

什麼情況用Volitale?

待續....