單例模式就是是一個類僅可以建立一個物件,在java中實現主要有兩種方式:餓漢式和懶漢式。
先看兩種方式共有的部分,因為是單例,所以構造方法必須是私有的private,而且必須提供一個對外界開放的獲取物件的方法,該方法內部控制返回唯一的一個物件例項:
public class Singleton { //構造方法私有,阻斷外界直接建立物件的方法 private Singleton() {} //提供一個獲取唯一物件例項的方法 public static Singleton getInstance(){ //... } }
以上是不管什麼方式的實現,都得遵循的一些規定,下面就介紹懶漢式和餓漢式:
餓漢式是單例類感覺自己很飢餓(將例項物件想象為吃的),不管有沒有別的類跟我要例項類,我都要自己先生成一個,像下面的實現:
public class Singleton { //構造方法私有,阻斷外界直接建立物件的方法 private Singleton() {} //內部持有一個類初始化時唯一建立的一個類例項物件 private static Singleton singleton = new Singleton(); //提供一個獲取唯一物件例項的方法 public static Singleton getInstance(){ return singleton; } }
餓漢式的有點在於編碼邏輯簡單好理解,無執行緒安全問題;缺點嘛,自然就是當我僅僅是想用Singleton類其他方法,他還是建立了一個對我現在來說沒用的例項物件,當系統中這種餓漢式的單例類多起來的時候,無疑是一種資源浪費,這個問題正好懶漢式可以解決。
懶漢式,就是自己首先持有一個空的單例類的例項,但是不會類一載入就建立例項,只有當別人第一次要我的例項物件我才給他建立,懶嘛,要是沒人要我就不建立了:
public class Singleton { //構造方法私有,阻斷外界直接建立物件的方法 private Singleton() {} //內部持有一個單例類的引用 private static Singleton singleton = null; //提供一個獲取唯一物件例項的方法 public static Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
餓漢式的有點不用說了,就是解決了懶漢式的缺點;但是他的缺點就是多執行緒環境下,不盡人意啊,比如兩個執行緒同時都是第一次去獲取Singleton類的例項的時候,又同時執行到if(singleton==null)這一行,兩個執行緒就會同時進入if語句執行體中去,可能先後執行了singleton = new Singleton();這就違反了單例模式的概念,所以還得想個辦法解決這個問題。
執行緒安全的餓漢式:(其實只要將if語句上加上執行緒鎖,就可以避免兩個執行緒一起跑到這個地方來了)
public class Singleton { //構造方法私有,阻斷外界直接建立物件的方法 private Singleton() {} //內部持有一個單例類的引用 private static Singleton singleton = null; //提供一個獲取唯一物件例項的方法 public static Singleton getInstance(){ synchronized (Singleton.class) { if(singleton==null){ singleton = new Singleton(); } } return singleton; } }
踏噠~,解決了,但是!你發現了麼?這樣的話,每次別人想獲取sinleton例項的時候都得等待別的執行緒釋放鎖,自己再加鎖,自己再釋放鎖,而這些鎖的操作又是那麼消耗時間,能不能再優化一下。想一想,是不是第一次訪問完了,只要sington物件例項,不為空,直接返回就是了,沒有現成問題啊,應該這樣:
public class Singleton { //構造方法私有,阻斷外界直接建立物件的方法 private Singleton() {} //內部持有一個單例類的引用 private static Singleton singleton = null; //提供一個獲取唯一物件例項的方法 public static Singleton getInstance(){ if(singleton==null){ synchronized (Singleton.class) { if(singleton==null){ singleton = new Singleton(); } } } return singleton; } }
完美!執行緒安全的懶漢式,既解決了資源浪費問題,又兼顧了執行緒安全問題。
不過還有個更巧妙地方法,這個就涉及到內部類的只是,當一個類載入的時候,其內部類並不載入,而是隻要第一次用到內部類的時候內部類才會載入,而且如果這個內部類是static內部類,也就是說載入了這個類一次,以後就直接獲取他就可以了,詳細參見另一篇部落格:http://www.cnblogs.com/WreckBear/p/5812942.html。
public class Singleton { //構造方法私有,阻斷外界直接建立物件的方法 private Singleton() {} /* * 搭建一個內部靜態類,外部類載入的時候,內部類並不會載入, * 只有當內部類被訪問的時候才會被載入、初始化,載入之後就會一直儲存在記憶體中 */ public static class Get{ public static Singleton singleton = new Singleton(); } }
在Main中獲取:
public class Main { public static void main(String[] args) { Singleton singleton = Singleton.Get.singleton; } }
這種方法顯得更加優雅一點,至此,結束!