設計模式(一):單例模式

考慮這樣一個應用,讀取配置文件的內容。不少應用項目,都有與應用相關的配置文件,這些配置文件不少是由項目開發人員自定義的,在裏面定義一些應用重要的參數數據。固然,在實際的項目中,這種配置文件多數採用 xml 格式,也有采用 properties 格式的,咱們這裏假設建立了一個名爲 AppConfig 的類,它專門用來讀取配置文件內的信息。客戶端經過 new 一個 AppConfig 的實例來獲得一個操做配置文件內容的對象。若是在系統運行中,有不少地方都須要使用配置文件的內容,也就是說不少地方都須要建立 AppConfig 對象的實例。換句話說,在系統運行期間,系統中會存在不少個 AppConfig 的實例對象,這裏讀者有沒有發現有什麼問題存在?固然有問題了,試想一下,每個 AppConfig 實例對象裏面都封裝着配置文件的內容,系統中有多個 AppConfig 實例對象,也就是說系統中會同時存在多份配置文件的內容,這樣會嚴重浪費內存資源。若是配置文件內容越多,對於系統資源的浪費程度就越大。事實上,對於 AppConfig 這樣的類,在運行期間只須要一個實例對象就足夠了。java

從專業化來講,單例模式是一種對象建立模式,它用於產生一個對象的具體實例,它能夠確保系統中一個類只產生一個實例。Java 裏面實現的單例是一個虛擬機的範圍,由於裝載類的功能是虛擬機的,因此一個虛擬機在經過本身的 ClassLoad 裝載實現單例類的時候就會建立一個類的實例。在 Java 語言中,這樣的行爲能帶來兩大好處:多線程

  1. 對於頻繁使用的對象,能夠省略建立對象所花費的時間,這對於那些重量級對象而言,是很是可觀的一筆系統開銷;函數

  2. 因爲 new 操做的次數減小,於是對系統內存的使用頻率也會下降,這將減輕 GC 壓力,縮短 GC 停頓時間。性能

所以對於系統的關鍵組件和被頻繁使用的對象,使用單例模式能夠有效地改善系統的性能。單例模式的核心在於經過一個接口返回惟一的對象實例。首要的問題就是要把建立實例的權限收回來,讓類自身來負責本身類的實例的建立工做,而後由這個類來提供外部能夠訪問這個類實例的方法。spa

一、單例模式的基本實現

首先單例類必需要有一個 private 訪問級別的構造函數,只有這樣,才能確保單例不會在系統中的其餘代碼內被實例化,;其次,instance 成員變量和 getInstance 方法必須是 static 的。線程

public class Singleton {
   private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInsatnce() { return instance; } }

上述代碼惟一的不足是沒法對 instance 實例作延時加載,例如單例的建立過程很慢,而因爲 instance 成員變量是 static 定義的,所以在 JVM 加載單例類時,單例對象就會被創建,若是此時這個單例類在系統中還扮演其餘角色,那麼在任何使用這個單例類的地方都會初始化這個單例變量,而不論是否會被用到。code

二、延遲加載的單例模式

爲了解決這類問題,須要引入延遲加載機制xml

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

首先對於靜態成員變量 instance 初始化賦值 null,確保系統啓動時沒有額外的負載;其次,在 getInstance() 工廠方法中,判斷當前單例是否已經存在,若存在則返回,不存在則再創建單例。這裏尤爲要注意的是,getInstance() 方法必須是同步的,不然在多線程環境下,當線程 1 正新建單例時,完成賦值操做前,線程 2 可能判斷 instance 爲 null,故線程 2 也將啓動新建單例的程序,而致使多個實例被建立,故同步關鍵字是必須的。因爲引入了同步關鍵字,致使多線程環境下耗時明顯增長。對象

三、靜態內部類的單例模式

解決同步關鍵字下降系統性能的缺陷,可使用靜態內部類,這種方法也是《Effective Java》上所推薦的。blog

public class Singleton { private Singleton (){} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }

當 StaticSingleton 被加載時,其內部類並不會被初始化,故能夠確保當 StaticSingleton 類被載入 JVM 時,不會初始化單例類,而當 getInstance() 方法調用時,纔會加載 SingletonHolder,從而初始化 instance。同時,因爲實例的創建是時在類加載時完成,故天生對多線程友好,getInstance() 方法也無需使用同步關鍵字。

本文主要內容來自連接https://www.ibm.com/developerworks/cn/java/j-lo-Singleton/

相關文章
相關標籤/搜索