資料借鑒:http://cantellow.iteye.com/blog/838473 簡單介紹: 單例模式是一種經常用到的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中應用該模式的類只有一個實例。即一個類只有一個實例 定義: 一個類有且只有一個實例,並且自行 ...
資料借鑒:http://cantellow.iteye.com/blog/838473
簡單介紹: 單例模式是一種經常用到的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中應用該模式的類只有一個實例。即一個類只有一個實例 定義: 一個類有且只有一個實例,並且自行實例化向整個系統提供。 特點: 1、單例類只能有一個實例。 2、單例類必須自己創建自己的唯一實例。 3、單例類必須給所有其他對象提供這一實例。 實現方式: 一般來說,我們有以下幾個必要的操作: 1、私有化構造方法; 2、final類(定義為不可繼承,這點書上沒有提到,暫時還在研究) 3、將類的實例對象定義為一個私有的屬性(不限製為成員變數) 4、通過getInstance()方法獲取實例,若私有的實例屬性對象引用不為空則返回,否則實例化該屬性並返回 這裡先介紹四種實現方式 1、餓漢模式 2、懶漢模式 3、雙重驗證 4、靜態內部類模式 先看第一種方式,餓漢模式顧名思義,迫不及待的想要吃(初始化實例),在類中定義一個私有靜態本類的實例化對象,在類載入的過程就進行此對象的實例化,之後的對此類實例的調用都是調用此實例。 代碼如下:public class Singleton2 { private static Singleton2 singleton2= new Singleton2(); private Singleton2(){} public static Singleton2 getInstance() { return singleton2; } public static String getStr() { return "Create String type Object"; } }餓漢模式是較為簡單的實現方式,同樣也是較為常用的方式。但他有著一定的缺陷:雖然在單例模式中大多都只調用getInstance()方法,但不排除有其他的方式導致類載入,比如如果類中getStr()這種與類的實例無關的方法如果被調用,就會觸發類載入,從而對靜態成員進行初始化,但是此類有可能並不需要實例化,這樣在某種程度上會造成一定的資源浪費。也就無法達到lazy loading的效果。 這就引入了懶漢模式 懶漢模式:只有在第一此調用類的實例對象時才會初始化類的實例,從而實現延遲載入 代碼如下
public class Singleton3 { private static Singleton3 singleton2 = null; private Singleton3(){ Tools.println("類實例化"); } public static synchronized Singleton3 getInstance(){ if(singleton2 == null) singleton2 = new Singleton3(); return singleton2; } public static void CreateString(){ Tools.print("Create String in Singleton3"); } }懶漢模式通過getInstance()方法創建實例,這樣只有在使用到實例的時候才會初始化實例對象,實現了延遲載入,但是這種模式會出現線程同步問題:一個線程調用了getInstace()方法,判斷為空後進行實例的創建,此時又有了一個線程調用了getInstance()方法,但此刻第一個線程還沒有完成實例化的操作,故此線程也會實例化一個對象。所以我們需要為getInstance()方法加上同步關鍵字synchronized 。 那麼問題來了,我們使用延遲載入就是為了提升系統性能,而引入了同步關鍵字則會大大影響多線程情況下的性能,所以此方式也有這很大的缺陷。 下麵就引入了雙重檢測方式 雙重檢測方式:通過雙重檢測的方式完成延遲載入 代碼如下:
class Singleton1 { private Singleton1() { } public static Singleton1 instance = null; public static Singleton1 getInstance() { if (instance == null) { synchronized (Singleton1.class) { if (instance == null) { instance = new Singleton1(); } } } return instance; } }可以看到,首先判斷實例對象是否為空,如果判斷通過再進行同步操作。 這種方式是解決了懶漢模式的效率問題,但同時也有一些問題,第一次載入時反應不快,由於java記憶體模型一些原因偶爾失敗。失敗原因可以詳見http://blog.csdn.net/chenchaofuck1/article/details/51702129 接下來引入一種已經較為完善並且使用較多的一種實現方式:靜態內部類實現 代碼如下: public class Singleton4 { private Singleton4(){} static class SingletonHolder { private static Singleton4 singleton = new Singleton4(); } public static Singleton4 getInstance(){ return SingletonHolder.singleton; } } 此方式利用了:靜態內部類並不會在外部類類載入的時候也進行類載入,而是在它自身第一次被使用時進行類載入,並且jvm的類載入過程是對線程非常友好的,所以我們不需要擔心同步問題。
public class Singleton4 { private Singleton4(){} static class SingletonHolder { private static Singleton4 singleton = new Singleton4(); } public static Singleton4 getInstance(){ return SingletonHolder.singleton; } }上述方法都是基本上實現了單例模式,但是依然有兩個問題需要註意: 1:如果單例由不同的類裝載器註入,那邊有可能存在有多個單例類的實例。假如不是遠端存取,假如一些servlet容器對每個servlet使用不同的類裝載器,他們就會有個字的單例類的實例。 2:如果單例類實現了java.io.Serializable介面,那麼此類的實例就可以被序列化和複原,如果序列化一個對象,然後複原多個此對象,就會出現多個單例類的實例。 關於此問題可以參照此文章:http://blog.csdn.net/fg2006/article/details/6409423 第一個問題的解決方法:
private static Class getClass(String classname) throws ClassNotFoundException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if(classLoader == null) classLoader = Singleton.class.getClassLoader(); return (classLoader.loadClass(classname)); } }第二個問題的解決方法:
public class Singleton implements java.io.Serializable { public static Singleton INSTANCE = new Singleton(); protected Singleton() { } private Object readResolve() { return INSTANCE; } }這兩種方法是我從其他的博客上看來的,現在還在瞭解中。。。 應用場景: 1. Windows的Task Manager(任務管理器)就是很典型的單例模式(這個很熟悉吧),想想看,是不是呢,你能打開兩個windows task manager嗎? 不信你自己試試看哦~ 2. windows的Recycle Bin(回收站)也是典型的單例應用。在整個系統運行過程中,回收站一直維護著僅有的一個實例。 3. 網站的計數器,一般也是採用單例模式實現,否則難以同步。 4. 應用程式的日誌應用,一般都何用單例模式實現,這一般是由於共用的日誌文件一直處於打開狀態,因為只能有一個實例去操作,否則內容不好追加。 5. Web應用的配置對象的讀取,一般也應用單例模式,這個是由於配置文件是共用的資源。 6. 資料庫連接池的設計一般也是採用單例模式,因為資料庫連接是一種資料庫資源。資料庫軟體系統中使用資料庫連接池,主要是節省打開或者關閉資料庫連接所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因為何用單例模式來維護,就可以大大降低這種損耗。 7. 多線程的線程池的設計一般也是採用單例模式,這是由於線程池要方便對池中的線程進行控制。 8. 操作系統的文件系統,也是大的單例模式實現的具體例子,一個操作系統只能有一個文件系統。 9. HttpApplication 也是單位例的典型應用。熟悉ASP.Net(IIS)的整個請求生命周期的人應該知道HttpApplication也是單例模式,所有的HttpModule都共用一個HttpApplication實例. 總結以上,不難看出: 單例模式應用的場景一般發現在以下條件下: (1)資源共用的情況下,避免由於資源操作時導致的性能或損耗等。如上述中的日誌文件,應用配置。 (2)控制資源的情況下,方便資源之間的互相通信。如線程池等。