1. 程式人生 > >如何創建一個對象(二、單例)

如何創建一個對象(二、單例)

最簡 初始化 修飾 就是 裏的 這一 tin 並且 資源

為什麽需要單例模式

在應用程序中,經常會用到單例模式,即這個類只能存在一個對象實例。
那麽為什麽需要這種模式,我們在一個程序應用中,只需要創建一次性的對象實例以節省內存資源,避免重復創建的開銷,以便後面使用可以更快的訪問。

如何寫一個單例模式

  單例作為所有設計模式中最簡單的設計模式之一,其創建是非常簡單的。

餓漢式單例

#code 餓漢式單例-不推薦
public final class HungrySingleton {

    private byte[] data = new byte[1024];

    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}

  以上就是一個典型的餓漢式單例模式,在我們調用HungrySingleton. getInstance()方法時,不僅僅獲取了一個已經創建的對象,並且獲取了已經初始化了的1k的類變量數據。
即便在多線程環境下,instance也不會被創建兩次,因為在類初始化的時候被收集進

懶漢式單例

#code 懶漢式單例-不推薦
public final class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {
    }

    public static synchronized LazySingleton getInstance() {
        if (null == instance) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

上面這一段代碼其實就是懶漢式單例,我們在真正調用getInstance()方法的時候才去創建這個實例,這個類所需的資源到這個時候才會被開辟。我們同時也使用synchronized來保證多線程環境下只有一份實例。
看起來很美妙,但可惜的是,這個方式也同樣不被推薦。原因也很簡單,因為我們在使用getInstance()的時候是同步的,意味著每個調用該方法的線程都必須阻塞等待其他線程調用完成,這一點就很耗費性能。

Double Check

#code 雙重檢查式單例-不推薦
public final class DCSingleton {

    private static volatile DCSingleton instance;

    private DCSingleton() {
    }

    public static DCSingleton getInstance(){
        if (null == instance){
            synchronized (DCSingleton.class){
                if (null == instance){
                    instance = new DCSingleton();
                }
            }
        }
        return instance;
    }
}

在我很長的一段時間內以來,在創建單例實例的時候都喜歡使用這種方式。它既保證了線程安全,也保證了延遲加載,同時相比懶漢式單例的耗費性能,它使用的雙重檢查的技巧很大程度上緩解了性能浪費,而且volatile修飾的instance也不會被指令重排困擾。看上去很完美,從一定程度上來說的確是這樣。
直到我看了doug lea與人合著的那本並發實踐書籍,原文是這樣的:“DCL這種使用方法已經被廣泛的廢棄了——促使該模式出現的驅動力不復存在,因而它不是一種高效的優化措施。延遲初始化占位類模式能帶來同樣的優勢,並且更容易理解。”這裏的驅動力是指,在新的版本下,無競爭同步的執行速度變快了(以前很慢),JVM的啟動速度也變快了(以前很慢)。

延遲初始化占位類模式

#code Holder式單例-推薦max
public final class HolderSingleton {

    private HolderSingleton() {
    }

    private static class Holder {
        private static HolderSingleton instance = new HolderSingleton();
    }

    public static HolderSingleton getInstance() {
        return Holder.instance;
    }
}

從線程安全、高性能、懶加載來看,這個應該是目前最好的單例設計之一,也是使用最為廣泛的一個。

枚舉式

#code Holder式單例-酌情使用
public enum EnumSingleton {
    INSTANCE;

    EnumSingleton() {
        System.out.println("complete init...");
    }

    public static EnumSingleton getInstance() {
        System.out.println("getInstance...");
        return INSTANCE;
    }

    public static void doneTask() {
        System.out.println("doneTask...");
    }
}

在《Effective Java》那本書中,這個枚舉方式實現單例的方式就被極為推薦。枚舉方式不允許被繼承,同樣也只是被實例化一次,但是不能夠懶加載。所以讀者可以酌情使用。

如何創建一個對象(二、單例)