單例模式是23種設計模式之一,是比較簡單的一種設計模式,它的目的是無論調用多少次,都返回同一個對象,它的特點是構造器私有化。
它分為兩種結構,一種是懶漢式的,一種是餓漢式的,它們各有優缺點,我們先從餓漢式看起,代碼如下:
public class Single { private static Single single = new Single(); private Single() { } public Single getInstance() { return single; } }
透過上面的程式可以看出來雖然我們載入同一個物件的目的確實達到了,但當程式被載入的時候就會創建single這個對象,當這個類別有多個這樣的方法時,我們可能會用不到這個物件中大多數單例,就會造成對記憶體的浪費。所以就出現了懶漢式的單例模式,程式碼如下:
public class Single { private static Single single = null; private Single() { } public Single getInstance() { if(single==null){ single = new Single(); } return single; } }
這樣,只有當我們真正呼叫這個物件時它才會被new出來,但是這樣是存在問題的。
當上面的第二段程式碼在第一次加載的時候有兩個線程對其進行了調用,則會產生兩個不同的對象,所以是線程不安全的,這時候就會想到給這個方法加個鎖,加鎖之後的程式碼如下:
public class Single { private static Single single = null; private Single() { } public synchronized Single getInstance() { if (single == null) { single = new Single(); } return single; } } }
這樣做確實做到了線程安全,但是當加鎖這個方法裡面要執行很多東西,調用這個方法花費的時間會很長,這樣對伺服器來說是致命的,因為這個方法如果某個線程一直調用的話,其它的執行緒是沒有辦法調的,伺服器就阻塞了,那麼升級後的程式碼如下:
public class Single { priate static Single single = null; private Single() { } public Single getInstance() { if (single == null) { synchronized (Single.class) { single = new Single(); } } return single; } }
仔細觀察以後發現這樣並沒有鎖住,當第一次同時有兩個線程到達getInstance()方法if判斷時,其中有一個肯定是阻塞的,當另外一個執行完以後,阻塞這個線程是不會再判斷是否為空的,還是會創建一個物件的,這樣又有多個物件被產生了,再對其進行升級,得到的程式碼如下:
public class Single { private static Single single = null; private Single() { } public Single getInstance() { if (single == null) { synchronized (Single.class) { if (single == null) { synchronized (Single.class) { if (single == null) { single = new Single (); } } } return single; } }
這樣就不會產生上面的問題,也只鎖一次,因為第二次再執行這個方法時,會跳過if判斷,直接返回single,不會再被鎖,執行效率也會很高。
但即使是這樣,也還是有問題的,因為我們不能確定在內存中是先給對象賦值,還是先創建了這個對象,所以第二個程序有可能得到的是初始化一半了的對象,在jdk1. 5之後,我們可以用volatile這個關鍵字來避免這種情況,程式碼如下:
public class Single { private static volatile Single single = null; private Single() { } public Single getInstance() { if (single == null) { synchronized (Single.class) { if (single == null) { synchronized (Single.class) { if (single == null) { single = class) { if (single == null) { single = class) { if (single == null) { single = class) Single(); } } } return single; } }
但是這種情況很少使用,我在這裡只是為了學習一下,嘻嘻