設計模式 單例模式(Singleton)

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。數據庫

單例模式5種建立方式:windows

  • 餓漢式:類初始化的時候,會當即加載該對象,線程天生安全,調用效率高。
  • 懶漢式:類初始化時,不會初始化該對象,真正須要使用的時候纔會去建立該對象,具有懶加載功能。
  • 靜態內部類方式:結合了懶漢式和餓漢式各自的優勢,真正須要對象的時候纔會加載,加載類是線程安全的。
  • 枚舉單例:使用枚舉實現單例模式,實現簡單、調用效率高,枚舉自己就是單例,由JVM從根本上提供保障,避免經過反射和反序列化的漏洞,缺點是沒有延遲加載。
  • 雙重檢測方式(由於JVM自己重排序的緣由,可能會出現屢次的初始化)。

 實現代碼以下:設計模式

//餓漢式
public class SingletonDemo01 {
    // 類初始化時,會當即加載該對象,線程天生安全,調用效率高
    private static SingletonDemo01 singletonDemo01 = new SingletonDemo01();
    private SingletonDemo01() {
        System.out.println("SingletonDemo01初始化");
    }
    public static SingletonDemo01 getInstance() {
        System.out.println("getInstance");
        return singletonDemo01;
    }
    public static void main(String[] args) {
        SingletonDemo01 s1 = SingletonDemo01.getInstance();
        SingletonDemo01 s2 = SingletonDemo01.getInstance();
        System.out.println(s1 == s2);//結果爲ture
    }
 
}

 

//懶漢式
public class SingletonDemo02 {
    //類初始化時,不會初始化該對象,真正須要使用的時候纔會建立該對。
    private static SingletonDemo02 singletonDemo02;
 
    private SingletonDemo02() {
    }
    public synchronized static SingletonDemo02 getInstance() {
        if (singletonDemo02 == null) {
            singletonDemo02 = new SingletonDemo02();
        }
        return singletonDemo02;
    }
 
    public static void main(String[] args) {
        SingletonDemo02 s1 = SingletonDemo02.getInstance();
        SingletonDemo02 s2 = SingletonDemo02.getInstance();
        System.out.println(s1 == s2);//結果爲true
    }
}

 

//靜態內部類方式
public class SingletonDemo03 {
    private SingletonDemo03() {
           System.out.println("初始化..");
    }
 
    public static class SingletonClassInstance {
        private static final SingletonDemo03 singletonDemo03 = new SingletonDemo03();
    }
 
    // 方法沒有同步
    public static SingletonDemo03 getInstance() {
        System.out.println("getInstance");
        return SingletonClassInstance.singletonDemo03;
    }
 
    public static void main(String[] args) {
        SingletonDemo03 s1 = SingletonDemo03.getInstance();
        SingletonDemo03 s2 = SingletonDemo03.getInstance();
        System.out.println(s1 == s2);//結果爲true
    }
}
//
優點:兼顧了懶漢模式的內存優化(使用時才初始化)以及餓漢模式的安全性(不會被反射入侵)。
//劣勢:須要兩個類去作到這一點,雖然不會建立靜態內部類的對象,可是其 Class 對象仍是會被建立,並且是屬於永久帶的對象。
 

 

//枚舉單例式
public class User {
    public static User getInstance() {
        return SingletonDemo04.INSTANCE.getInstance();
    }
 
    private static enum SingletonDemo04 {
        INSTANCE;
        // 枚舉元素爲單例
        private User user;
 
        private SingletonDemo04() {
            System.out.println("SingletonDemo04");
            user = new User();
        }
 
        public User getInstance() {
            return user;
        }
    }
 
    public static void main(String[] args) {
        User u1 = User.getInstance();
        User u2 = User.getInstance();
        System.out.println(u1 == u2);//結果爲true
    }
}

 

//雙重檢測方式
public class SingletonDemo04 {
    private SingletonDemo04 singletonDemo04;
 
    private SingletonDemo04() {
 
    }
 
    public SingletonDemo04 getInstance() {
        if (singletonDemo04 == null) {
            synchronized (this) {
                if (singletonDemo04 == null) {
                    singletonDemo04 = new SingletonDemo04();
                }
            }
        }
        return singletonDemo04;
    }
 
}

 

優勢: 安全

  • 在單例模式中,活動的單例只有一個實例,對單例類的全部實例化獲得的都是相同的一個實例。這樣就 防止其它對象對本身的實例化,確保全部的對象都訪問一個實例。 
  • 單例模式具備必定的伸縮性,類本身來控制實例化進程,類就在改變實例化進程上有相應的伸縮性。
  • 提供了對惟一實例的受控訪問。
  • 因爲在系統內存中只存在一個對象,所以能夠 節約系統資源,當 須要頻繁建立和銷燬的對象時單例模式無疑能夠提升系統的性能。
  • 容許可變數目的實例。
  • 避免對共享資源的多重佔用。


缺點: 多線程

  • 不適用於變化的對象,若是同一類型的對象老是要在不一樣的用例場景發生變化,單例就會引發數據的錯誤,不能保存彼此的狀態。
  • 因爲單利模式中沒有抽象層,所以單例類的擴展有很大的困難。
  • 單例類的職責太重,在必定程度上違背了「單一職責原則」。
  • 濫用單例將帶來一些負面問題,如爲了節省資源將數據庫鏈接池對象設計爲的單例類,可能會致使共享鏈接池對象的程序過多而出現鏈接池溢出;若是實例化的對象長時間不被利用,系統會認爲是垃圾而被回收,這將致使對象狀態的丟失。

 

注意事項:工具

  • 使用時不能用反射模式建立單例,不然會實例化一個新的對象。
  • 使用懶單例模式時注意線程安全問題。
  • 單例模式和懶單例模式構造方法都是私有的,因此不能被繼承,有些單例模式能夠被繼承(如登記式模式)。


應用場景:
單例模式只容許建立一個對象,所以節省內存,加快對象訪問速度,所以對象須要被公用的場合適合使用。性能

  • 多個模塊使用同一個數據源鏈接對象。
  • 須要頻繁實例化而後銷燬的對象。
  • 建立對象時耗時過多或者耗資源過多,但又常常用到的對象。
  • 有狀態的工具類對象。
  • 頻繁訪問數據庫或文件的對象。
  • 資源共享的狀況下,避免因爲資源操做時致使的性能或損耗等。如日誌文件,應用配置。
  • 控制資源的狀況下,方便資源之間的互相通訊。如線程池。


舉例: 優化

  • 外部資源:每臺計算機有若干個打印機,但只能有一個PrinterSpooler,以免兩個打印做業同時輸出到打印機。
  • 內部資源:大多數軟件都有一個(或多個)屬性文件存放系統配置,這樣的系統應該有一個對象管理這些屬性文件。
  • Windows的TaskManager(任務管理器)就是典型的單例模式,想一想看,你能同時打開兩個windows task manager嗎? 
  • windows的Recycle Bin(回收站)也是典型的單例模式。在整個系統運行過程當中,回收站一直維護着僅有的一個實例。
  • 網站的計數器,通常也是採用單例模式實現,不然難以同步。
  • 應用程序的日誌應用,通常都是使用單例模式實現,由於共享的日誌文件一直處於打開狀態,所以只能有一個實例去操做,不然不方便追加內容。
  • Web應用的配置對象的讀取,通常也應用單例模式,由於配置文件是共享的資源。
  • 數據庫鏈接池的設計通常是採用單例模式,由於數據庫鏈接是一種數據庫資源。數據庫軟件系統中使用數據庫鏈接池,主要是節省打開或者關閉數據庫鏈接所引發的效率損耗,這種效率上的損耗仍是很是昂貴的,使用單例模式來維護,能夠大大下降這種損耗。
  • 多線程的線程池的設計通常也是採用單例模式,由於線程池要方便對池中的線程進行控制。
  • 操做系統的文件系統,也是單例模式實現的具體例子,一個操做系統只能有一個文件系統。
相關文章
相關標籤/搜索