1. 程式人生 > >《大話設計模式》讀書筆記:單例模式與Java同步鎖synchronized

《大話設計模式》讀書筆記:單例模式與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方法時,都需要加鎖,與上例相比效率更低。