Java設計思想之單例模式
單例模式(Singleton Pattern)是Java中最常見的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。
這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有的那個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。
- 單例類只能有一個例項。
- 單例類必須自己建立自己的唯一例項。
- 單例類必須給所有其他物件提供這一例項。
設計單例的意圖:保證一個類僅有一個例項,並提供一個訪問他的全域性訪問點。主要解決:一個全年局使用的類頻繁地建立和銷燬。控制例項數目,節省系統資源。判斷系統是否已經有這個單例,如果有則返回,如果沒有則建立。關鍵程式碼:建構函式是私有的。優點:在記憶體裡只有一個例項,減少記憶體的開銷,尤其是頻繁的建立和銷燬例項。避免對資源的多重佔用。缺點是:沒有介面,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來例項化。使用的場景:要求生產唯一序列號。計數器,不用每次重新整理都在資料庫里加一次,用單例先快取起來。建立一個物件需要消耗的資源過多,比如I/O與資料庫的連線等。注意:getInstance()方法中需要使用同步鎖synchronized防止多執行緒同時進入造成instance被多次例項化。
單例型別的類建構函式是私有的,然後有一個本身的靜態例項。有一個靜態方法,供外界獲取它的靜態例項。
public class SingleObject{ //建立類的一個物件 private static SingleObect instnce = new SingleObject() ; //私有建構函式,類不會被例項化 private SingleObject() {} //獲取唯一可用的物件 public static SingleObject getInstance(){ return instance ; } }
單例模式的實現由多種方式:
1、懶漢式,執行緒不安全:Lazy初始化:是,多執行緒安全:否,實現難度:易。
描述:這種方式是最基本的實現方式,這種實現的最大問題就是不支援多執行緒。因為沒有加鎖synchronized,所以嚴格意義上並不算單例模式。這種方式lazy loading很明顯,不要求執行緒安全,在多執行緒情況下不能正常工作。
public class Singleton{ private static Singleton instance ; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance ; } }
下面幾種實現方式都支援多執行緒,但在效能上有所差異。
2、餓漢式:
lazy 初始化:否,多執行緒安全:是,實現難度:易。
描述:這種方式比較常用,但容易產生垃圾物件。優點:沒有執行效率會高。缺點:類載入時就初始化,浪費記憶體。它基於calssloder機制避免了多執行緒的同步問題,不過,instance在類載入時就例項化,雖然導致裝載的原因很多,在單例模式中大多數都是getInstance方法,但是也不能確定有其它的方式(或者其它的靜態方法導致類載入),這時初始化instance顯然沒有達到lazy loading的效果。
3、雙檢鎖/雙重校驗鎖(DCL,double-checked locking)
lazy初始化:是,多執行緒安全:是,實現難度:較複雜。描述:這種方式採用雙鎖機制,安全且在多執行緒情況下保持高效能。getInstance()的效能對應用程式很關鍵。
public class Singleton{ private volatile static Singleton singleton ; private Singleton(){} public static Singleton getSingleton(){ if(singleton == null){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton ; } }
5、登記式/靜態內部類
Lazy初始化:是,多執行緒安全:是,實現難度:一般。描述:這種方式能達到雙檢鎖一樣的功效,但實現更簡單。對靜態域使用延遲初始化,應使用這種方式二不是雙檢鎖方式。這種方式只適用於靜態域的情況,雙檢鎖方式可在例項域需要延遲初始化時使用。這種方式同樣利用了classloder機制來保證初始化instance時只有一個執行緒,它跟第三種方式不同之處:第3中方式只要Singleton類被裝載了,instance救護被例項化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有顯式通過呼叫getInstance方法時,才會顯式裝載SingletonHolder類,從而例項化instance。
public class Singleton{ private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton(); } private Singleton(){} private static final Singleton getInstance (){ return SingletonHolder.INSTANCE ; } }