單例模式的意圖是為了確保一個類在整個JVM中有且僅有一個執行個體,並為他提供一個全域的訪問點。
單例模式機制
怎麼樣才能阻止其他開發人員建立類的新執行個體。
可以建立唯一一個建構函式,並設定為私人。
單例和線程
多線程延遲初始化一個單例模式盡量避免多個線程同時初始化該單例對象。
假設一個線程發現該單例對象為null,接著第二個線程運行也發現該單例對象為null,然後兩個線程都會對該單例對象進行初始化,為了避免這種競爭,需要使用鎖機制去協調不同線程對同一個方法的執行。
public class Singleton{ //持有私人靜態執行個體,防止被引用,此處賦值null,目的是實現消極式載入 private static Singleton instance=null; //構造私人構造方法,防止被執行個體化 private Singleton(){} //靜態工程方法,建立執行個體 public static Singleton getInstance(){ if(instance == null){ instance = new Singleton();}return instance;}}
此單例模式沒有安全執行緒,放在多線程環境中,一般只有在第一次建立對象的時候需要加鎖代碼如下:
public static Singleton getInstance(){ if(instance == null){ synchronized(instance){ if(instance == null){ instance = new Singleton();}}}return instance;}
此種情況可能出現問題,因為Java指令中建立對象和賦值是分開操作,也就是說instance = new Singleton()是分兩步操作的。
例如有A、B兩個線程:
1、A、B兩個線程同時進入到了if判斷,隨機到A線程先進行if判斷
2、此時instance為null,執行instance = new SingleTon()操作,由於JVM內部最佳化機制,JVM先分配給SingleTon執行個體的一些空白記憶體,並賦給instance成員(此時JVM並沒有執行個體化SingleTon),然後A離開synchronized
3、然後B線程計入synchronized模組,由於instance≠null,他會立馬離開程式塊,並將程式結果返回給該方法的程式。
4、此時B線程打算使用SingleTon執行個體,卻發現報錯了,他其實並沒有初始化
於是進一步最佳化:
public static class SingleTonFactory(){ private static SingleTon instance = new SingleTon(); public static SingleTon getInstance(){ return SingleTonFactory.instance;}}
此時SingleTon只會被執行個體化一次
在JVM中只有一個執行個體存在,對於某些需要頻繁建立的類,在大型系統中節省了大量開銷;省去了new操作,降低系統記憶體的利用率和GC的壓力;控制核心交易引擎,防止系統錯亂,保證核心伺服器獨立控制整個流程。