如何建立一個對象(2、單例)

爲何須要單例模式

在應用程序中,常常會用到單例模式,即這個類只能存在一個對象實例。
那麼爲何須要這種模式,咱們在一個程序應用中,只須要建立一次性的對象實例以節省內存資源,避免重複建立的開銷,以便後面使用能夠更快的訪問。java

如何寫一個單例模式

  單例做爲全部設計模式中最簡單的設計模式之一,其建立是很是簡單的。設計模式

餓漢式單例

#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也不會被建立兩次,由於在類初始化的時候被收集進 ()方法,該方法就是同步的。
可是這個有一個缺陷,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》那本書中,這個枚舉方式實現單例的方式就被極爲推薦。枚舉方式不容許被繼承,一樣也只是被實例化一次,可是不可以懶加載。因此讀者能夠酌情使用。優化

相關文章
相關標籤/搜索