Java設計模式-單例模式

2023-04-28 18:00:20

一、前言

單例模式是一種設計模式,它確保一個類只能建立一個範例,並提供一種全域性存取這個範例的方式。在Java中,單例模式可以通過多種方式來實現,其中最常見的是使用私有建構函式和靜態方法實現

二、基本語法

在Java中,實現單例模式的方式有多種,其中最常見的實現方式包括以下幾種:

1、懶漢式單例模式

懶漢式單例模式指的是在第一次使用單例物件時才建立範例。具體實現方式是在getInstance()方法中判斷範例是否已經被建立,如果沒有則建立一個新範例並返回。懶漢式單例模式的缺點是執行緒不安全,在多執行緒環境下可能會建立多個範例。

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {
        // 私有建構函式
    }
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

2、餓漢式單例模式

餓漢式單例模式指的是在類載入時就建立範例,因此也被稱為靜態單例模式。具體實現方式是將範例化語句放在靜態程式碼塊中。由於在類載入時就建立了範例,因此不存線上程安全性問題。

public class Singleton {
    private static Singleton instance = new Singleton();
    
    private Singleton() {
        // 私有建構函式
    }
    
    public static Singleton getInstance() {
        return instance;
    }
}

3、雙重檢驗鎖單例模式

雙重檢驗鎖單例模式是一種執行緒安全的單例模式實現方式,它通過使用synchronized關鍵字來確保執行緒安全性。具體實現方式是在getInstance()方法中新增雙重檢驗鎖,這可以避免不必要的鎖競爭和範例化。

public class Singleton {
    private static volatile Singleton instance;
    
    private Singleton() {
        // 私有建構函式
    }
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

4、靜態內部類單例模式

靜態內部類單例模式是一種比較常用的單例模式實現方式,它利用了靜態內部類只會在被使用時才會載入的特點,從而避免了餓漢式單例模式的資源浪費和懶漢式單例模式的執行緒不安全問題。

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    private Singleton() {
        // 私有建構函式
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

5、列舉單例模式

列舉單例模式是一種更為簡單和安全的單例模式實現方式,它利用了Java中列舉型別本身就是單例的特點。列舉單例模式是一種天然執行緒安全的單例模式實現方式,而且可以防止反射和序列化等攻擊。

public enum Singleton {
    INSTANCE;
    
    // 其他方法
}

6、ThreadLocal單例模式

ThreadLocal單例模式是一種可以在多執行緒環境下確保單例物件的執行緒安全單例模式實現方式。具體實現方式是在ThreadLocal中儲存單例物件,每個執行緒都有自己的ThreadLocal副本,從而避免了執行緒安全性問題。

public class Singleton {
    private static final ThreadLocal<Singleton> INSTANCE = new ThreadLocal<Singleton>() {
        @Override
        protected Singleton initialValue() {
            return new Singleton();
        }
    };
    
    private Singleton() {
        // 私有建構函式
    }
    
    public static Singleton getInstance() {
        return INSTANCE.get();
    }
}

7、註冊式單例模式

註冊式單例模式指的是通過一個登入檔來管理所有單例物件,從而實現單例模式。具體實現方式是在一個靜態的Map中儲存所有單例物件,然後在需要使用單例物件時通過Map來獲取。

public class Singleton {
    private static Map<String, Singleton> instances = new HashMap<>();
    
    private Singleton() {
        // 私有建構函式
    }
    
    public static Singleton getInstance(String name) {
        if (!instances.containsKey(name)) {
            instances.put(name, new Singleton());
        }
        return instances.get(name);
    }
}

三、使用場景

單例模式通常在需要確保全域性只有一個範例的場景中使用,例如:

  1. 執行緒池:在多執行緒環境下,執行緒池需要保證只有一個範例。
  2. 資料庫連線池:同樣地,資料庫連線池也需要保證只有一個範例。
  3. 紀錄檔物件:紀錄檔物件通常是全域性可見的,因此需要保證只有一個範例。
  4. 組態檔:在某些情況下,需要全域性共用的組態檔也需要保證只有一個範例。

四、使用範例

下面是一個簡單的例子,演示如何使用單例模式實現執行緒池:

public class ThreadPool {
    private static ThreadPool instance;
    
    private ThreadPool() {
        // 初始化執行緒池
    }
    
    public static synchronized ThreadPool getInstance() {
        if (instance == null) {
            instance = new ThreadPool();
        }
        return instance;
    }
    
    // 執行緒池相關的方法
}

在上述程式碼中,我們使用synchronized關鍵字來保證getInstance()方法的執行緒安全性。這意味著每次只有一個執行緒可以存取getInstance()方法,從而避免了多個執行緒同時建立執行緒池範例的問題。

四、常見問題

單例模式的實現有一些常見問題,需要注意:

  1. 執行緒安全性:如上所述,如果多個執行緒同時存取getInstance()方法,可能會導致多個範例的建立。因此,需要確保getInstance()方法是執行緒安全的,可以通過synchronized關鍵字來實現。
  2. 序列化問題:如果單例類實現了Serializable介面,那麼在反序列化時可能會建立多個範例。解決方法是在類中新增readResolve()方法,並返回單例範例。
  3. 反射問題:通過反射機制,可以呼叫私有建構函式建立範例。解決方法是在建構函式中新增判斷,如果已經存在範例則丟擲異常

五、總結

單例模式是一種非常常用的設計模式,在多執行緒環境下,它可以確保只有一個範例被建立,並提供一種全域性存取這個範例的方式。在Java中,可以通過私有建構函式和靜態方法實現單例模式。在實現單例模式時,需要注意執行緒安全性、序列化問題以及反射問題。儘管單例模式非常有用,但也有一些缺點,例如它可能導致程式碼變得更加複雜,而且在多執行緒環境下可能會影響效能。因此,在使用單例模式時需要根據具體情況進行權衡。