java面試之單例模式的實現方法(幾種單例模式)

說句實話, 這個問題把我問的一臉懵逼, 怎麼會有這種面試題? 你如果問我雙重檢查鎖的單利模式實現的利弊, 還ok, 第一次碰到, 網上百度下 , 真TMD有, 現在貼出來跟大家分享, 尤其注意 雙重檢查鎖的單利模式就好了!

 

 

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

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

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

單例模式的幾種寫法

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");
    }
}

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章