Java單例模式的6種實現方式詳解

為什麼使用單例模式

需要確保某個類隻要一個對象,或創建一個類需要消耗的資源過多,如訪問IO和數據庫操作等,這時就需要考慮使用單例模式瞭。

使用單例模式需要註意的關鍵點

  • 將構造函數訪問修飾符設置為private
  • 通過一個靜態方法或者枚舉返回單例類對象
  • 確保單例類的對象有且隻有一個,特別是在多線程環境下
  • 確保單例類對象在反序列化時不會重新構建對象

單例模式的幾種寫法

1. 餓漢式

/**
 * 餓漢式實現單例模式
 */
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance() {
        return instance;
    }
}

2. 懶漢式

/**
 * 懶漢式實現單例模式
 */
public class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    // synchronized方法,多線程情況下保證單例對象唯一
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

getInstance()方法中添加瞭synchronized關鍵字,使其變成一個同步方法,目的是為瞭在多線程環境下保證單例對象唯一。

優點: 隻有在使用時才會實例化單例,一定程度上節約瞭資源。

缺點: 第一次加載時要立即實例化,反應稍慢。每次調用getInstance()方法都會進行同步,這樣會消耗不必要的資源。這種模式一般不建議使用。

3. DCL(Double CheckLock)實現單例

/**
 * DCL實現單例模式
 */
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {
    }
    public static Singleton getInstance() {
        // 兩層判空,第一層是為瞭避免不必要的同步
        // 第二層是為瞭在null的情況下創建實例
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

優點: 資源利用率高,既能夠在需要的時候才初始化實例,又能保證線程安全,同時調用getInstance()方法不進行同步鎖,效率高。

缺點: 第一次加載時稍慢,由於Java內存模型的原因偶爾會失敗。在高並發環境下也有一定的缺陷,雖然發生概率很小。
DCL模式是使用最多的單例模式實現方式,除非代碼在並發場景比較復雜或者JDK 6以下版本使用,否則,這種方式基本都能滿足需求。

4. 靜態內部類

/**
 * 靜態內部類實現單例模式
 */
public class Singleton {
    private Singleton() {
    }
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
    /**
     * 靜態內部類
     */
    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }
}

第一次加載Singleton類時不會初始化instance,隻有在第一次調用getInstance()方法時,虛擬機會加載SingletonHolder類,初始化instance。

這方式既保證線程安全,單例對象的唯一,也延遲瞭單例的初始化,推薦使用這種方式來實現單例模式。

5. 枚舉單例

/**
 * 枚舉實現單例模式
 */
public enum SingletonEnum {
    INSTANCE;
    public void doSomething() {
        System.out.println("do something");
    }
}

默認枚舉實例的創建是線程安全的,即使反序列化也不會生成新的實例,任何情況下都是一個單例。

優點: 簡單!

6. 容器實現單例

import java.util.HashMap;
import java.util.Map;
/**
 * 容器類實現單例模式
 */
public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<String, Object>();
    public static void regsiterService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }
    public static Object getService(String key) {
        return objMap.get(key);
    }
}

SingletonManager可以管理多個單例類型,使用時根據key獲取對象對應類型的對象。這種方式可以通過統一的接口獲取操作,隱藏瞭具體實現,降低瞭耦合度。

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: