單例模式(含執行緒鎖關鍵字)

劍握在手發表於2013-11-12

單例就是隻有一個例子,只有一個物件,不允許別人再建立物件。

 

餓漢式(初始化即建立物件)

class Single{

  private static Single s = new Single();

  private Single(){}

  public static Single getInstance(){

    return s;

  }

}

 

懶漢式(方法被呼叫時,才建立物件,也叫做物件的延時載入)

class Single{

  private static Single s = null;

  private Single(){}

  public static Single getInstance(){//當多人同時呼叫此方法時有可能出狀況

    if(s == null)      //語句①

      s = new Single();//語句②

    return s;

  }

}

 

懶漢式看似省空間,卻有可能在多執行緒時出問題。

舉個只有兩個執行緒的例子:執行緒A被單核CPU執行到①,單核CPU切入執行緒B去執行①,仍然會通過判斷,此時A,B都會執行語句②。

 

改進後的安全懶漢式(低效,在方法上增加了執行緒鎖):

class Single{

  private static Single s = null;

  private Single(){}

  public synchronized static Single getInstance(){

    if(s == null)      //語句①

      s = new Single();//語句②

    return s;

  }

}

tips:

執行緒鎖就是synchronized後邊的引數,漢語版的API中作者稱之為物件監視器。執行緒鎖有兩個狀態,一個鎖住一個開啟,開啟的時候執行緒就能進去,關閉的時候,執行緒就會在門前等待,直到鎖開啟才會進去。 

synchronized相當於一個標示符表示它所跟隨的大括號內的內容是同步程式碼塊,執行這部分程式碼塊就要判斷執行緒鎖的狀態。

 

再次改進後最終的懶漢式(在方法內部增加執行緒鎖)

class Single{

  private static Single s = null;

  private Single(){}

  public static Single getInstance(){

    if(s == null){      //語句①

      synchronized (Single.class){ //語句② 這樣就會最多判斷兩次執行緒鎖

        if(s == null) //語句③

          s = new Single();//語句④

      }

    }

    return s;

  }

}

 

 

解析一下:執行緒A執行語句①通過,執行語句②通過,此時CPU切入執行緒B執行到語句①通過,執行到語句②未通過,

然後CPU切入執行緒A繼續執行,通過語句③和④並解除執行緒鎖,CPU再次切入執行緒B,此時會通過語句②,執行語句③,

如果沒有語句③又悲劇了。。。

 

總結,既然有這麼一個單例類,肯定你是要用它的,你要用它一定會開闢記憶體存放它的物件,

懶漢實在是浪費時間又沒什麼實際意義,所以建議選擇餓漢式的單例模式。

相關文章