《大話設計模式》讀書筆記:單例模式與Java同步鎖synchronized
單例模式,保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。在單例模式下,類本身負責儲存它的唯一例項,這個類必須保證沒有其他例項可以被建立,並且它可以提供一個訪問該例項的方法。單例模式的類中,構造方法(函式/體)被設為private,從而堵死了外部例項化該類的可能。同時,在類中提供一個靜態方法負責提供該類的唯一例項,並在例項不存在的情況下試圖初始化它。
下面的示例展示了單例模式的典型實現:
public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance(){ if (instance == null) { instance = new Singleton(); } return instance; } }
上述的示例中,單例類中的例項在第一次被引用時才被初始化,這種型別的單例類稱為“懶漢式”。單例模式有兩種型別,另一種叫“餓漢式”,是指單例類的例項在載入時就被初始化,如下所示:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance(){
return instance;
}
}
單例模式主要用在類需要且僅需要被初始化一次的場合,如UI介面中的工具欄、Android應用中的警告框等等。在單執行緒程式中,通過上述兩種方式生成的單例類就已經可以滿足要求了。但是在多執行緒環境下,情況就麻煩一些:如果多個執行緒同時例項化某懶漢式
關於Java的同步鎖關鍵字synchronized ,可以參見如下的連結:
簡單地說,synchronized 關鍵字的用法,主要分為兩類:
一是方法定義時,加在方法名之前,形如:synchronized methodName(params){ ... };
二是宣告同步塊,形如:synchronized(this) { … }。
其作用是給修飾的方法或者程式碼塊加上同步鎖。當一個執行緒執行到這個方法或者程式碼塊的時候,該部分被鎖定,之後的其它執行到這些程式碼的執行緒被阻塞,直到上一個執行緒執行完畢,釋放同步鎖。
如下的程式碼是執行緒安全的懶漢式單例類的實現:
public class Singleton {
private static Singleton instance;
private final static Object syncLock = new Object();
private Singleton() {
}
public static Singleton getInstance(){
if (instance == null) {
synchronized (syncLock) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
需要注意的是,synchronized同步塊處括號中的鎖定物件採用的是一個無關的Object類例項。將它作為鎖而不是通常synchronized所用的this,其原因是getInstance方法是一個靜態方法,在它的內部不能使用未靜態的或者未例項化的類物件(避免空指標異常)。同時也沒有直接使用instance作為鎖定的物件,是因為加鎖之時,instance可能還沒例項化(同樣是為了避免空指標異常)。
此外,單例類中不建議將getInstance方法修飾為synchronized方法,其原因是一旦這樣做了,其效果就跟《大話設計模式》書中21.5小節的示例是一樣的了。這種做法會在每次呼叫getInstance方法時,都需要加鎖,與上例相比效率更低。