單例模式解析

單例模式解析

​ 單例模式是設計模式中使用最多的模式之一,它是一種對象的建立模式,用於產生一個對象的實例,確保系統中一個類只有一個實例。在Java語言中,單例模式有如下好處:java

  • 對於頻繁使用的對象,單例省略了建立對象的時間,對於重量級對象而言,極大減小了系統開銷。
  • 因爲new的次數減小,所以減小了系統對內存的使用頻次,減輕了GC壓力,縮短了GC停頓時間。

所以對於系統的關鍵組件和頻繁使用的對象,能夠設計爲單例模式,減小系統的開銷,提升系統性能。設計模式

​ 單例模式的參與者不多,只有單例類和使用者,關係表以下:多線程

角色 做用
單例類 提供單例的工廠,返回單例
使用者 獲取並使用單例

​ 類圖以下:函數

實現方式一(餓漢):

單例模式的核心在於經過一個接口返回惟一實例化對象,簡單實現以下:性能

/** * 單例模式--餓漢(沒法作到延時加載) */
public class HungrySingleton {

    private static HungrySingleton instance = new HungrySingleton();

    /** * 私有構造方法 */
    private HungrySingleton(){
        System.out.println("建立了對象");
    }

    /** * 對外暴露的獲取惟一實例的接口 * @return */
    public static HungrySingleton getInstance(){
        return instance;
    }

    /** * 序列化,反序列化保證單例 * @return */
    private Object readResolve(){
        return instance;
    }
}
複製代碼

原理:單例類必須私有化構造方法,保證不會在系統其餘地方調用,其次instance成員變量和getInstance()方法必須是static修飾的。優化

注意:單例模式的這種實現方式很是簡單,十分可靠,惟一不足是沒法作到延時加載。假設單例的建立過程十分緩慢,因爲instance的成員變量是由static修飾的,在JVM加載單例類的時候,單例對象就會存在,若是單例類還在系統中扮演別的角色,那麼系統中任何使用單例類的地方都會初始化這個單例對象,而無論是否被用到。spa

實現方式二(懶漢):

爲了解決上述問題,並提升系統在相關函數調用的反應速度,就須要加入延時加載機制,懶漢模式。線程

/** * 單例模式--懶漢(效率低,延時加載) */
public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {
        System.out.println("LazySingleton is create");
    }

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

原理:首先,靜態成員變量instance賦值爲null,確保系統啓動時沒有額外負載,其次在getInstance()方法中判斷當前單例instance對象是否存在,存在則返回,不存在建立單例對象。設計

注意:getInstance()必須是線程同步的,不然在多線程條件下,當線程1新建單例完成賦值前,線程2可能判斷instance爲null,線程2也建立了單例對象,致使多個實例被建立,所以同步關鍵字是必須的。使用上述單例模式的實現方式,雖然實現了延時加載,可是和第一種實現(餓漢)相比,引入了同步關鍵字,所以在多線程場景下,加載速度遠遠大於第一種實現方式,影響系統性能。code

實現方式三(內部類):

繼續改進,建立內部類:

/** * 使用內部類來維護單例的實例,當StaticSingleton被加載時候,內部類並無被初始化 * (instance並無被初始化),調用getInstance()纔會被初始化。 */
public class StaticSingleton {

    private StaticSingleton(){
        System.out.println("StaticSingleton is create");
    }

    /** * 內部類,建立單例對象 */
    private static class StaticSingleHolder{
        private static StaticSingleton instance = new StaticSingleton();
    }

    public static StaticSingleton getInstance(){
        return StaticSingleHolder.instance;
    }
}
複製代碼

原理:在這個實現中,使用內部類來維護單例實例,當StaticSingleton被加載的時候,內部類沒有被初始化,能夠確保StaticSingleton加載到JVM中,不會初始化單例類,當調用getInstance()時纔會加載StaticSingleHolder,初始化instance對象,同時因爲實例的創建是在類加載時完成的,對線程友好,getInstance()不須要使用同步關鍵字。

注意:使用內部類實現的單例,既能夠實現延時加載也避免使用同步關鍵字,是比較完善的實現。可是若是經過反射機制強行調用私有構造方法,就會生成多個單例。同時序列化和反序列化可能破壞單例(餓漢代碼readResolve()方法),場景很少見,若是存在,多加註意。

[^《Java性能程序優化 讓你的Java程序更快、更穩定》 葛一鳴]

相關文章
相關標籤/搜索