標籤:
對於普通單線程單例來說,較為容易,只要避免建立多個對象即可,代碼如下:
public class Singleton { private static Singleton singleton = null; public static Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
這樣既可以避免建立多個對象消耗系統資源,也可以達到惰性載入,即在使用時才會建立對象;
在單線程時,該類可以很完美的解決單例問題,但是到了多線程,該類將會出現問題:
當兩個現成A和B 同時調用單例獲得執行個體時,若A進入getInstance()方法,判斷當前singleton為空白,則進入singleton=new Singeton(),並返回singleton,但是當A進入時,B同時調用,B也會判斷singleton為空白,因為A進入後並沒有初始化完成,所以B同樣會進入初始化代碼,進行初始化並返回singleton,這樣就出現問題了,並沒有實現單例操作。
所以在多線程環境下,我們需要對該代碼進行一點修改,那就是給getInstance方法加上同步鎖,使A和B不能同時進入該方法內,代碼如下:
public class Singleton { private static Singleton singleton = null; public static synchronized Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
經過修改後,我們發現在出現之前A和B 同時訪問getInstance方法時,若A先進入getInstance方法,由於sychronized的存在,B是不可能進入該方法的,會在A線程執行完成之前將該方法進行加鎖操作,執行完成之後才會允許B進入該方法,而當B進入方法時,singleton已經不再是null,直接返回單例singleton對象;
但是實際上,若多個線程調用時,同步鎖的存在,很大程度上影響程式效能,所以後來有人提出double-checked blocking方法,來降低效能影響:
還有一種方法,叫做double-check blocking,代碼如下:
public class Singleton { private static Singleton singleton = null; public static Singleton getInstance(){ if(singleton==null){ synchronized (singleton){ if(singleton==null){ singleton = new Singleton(); } } } return singleton; } }
經過此次修改,當線程進入getInstance方法後,將會先判斷singleton是否為空白,這樣減少了進入同步塊所花費的資源,降低了資源消耗,提高了效能,
似乎這樣修改以後,即不會造成同步鎖消耗資源,也不會由於多線程同時進入建立多個對象,但是,實際上,從更深一層來講,站在jvm的層面來說,這樣的代碼仍可能發生問題:
由於在執行singleton = new Singleton();時,jvm是分兩步進行的,先是為singleton預留空間,直接賦值給instance,然後才會初始化singleton,建立singleton執行個體,
如果A先進入singleton = new sSingleton(),但是只是分配了空間,並沒有初始化完成,就返回singleton,那麼當B線程進入時,singleton已經不為null,那麼將直接返回已有的singleton,若在singleton真正初始化之前就使用的話,問題就來了,所有 多線程單例模式,又出現了新的解決辦法:
public class Singleton{ private static c lass SingletonContainer{ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return SingletonContainer.instance; }}
該方法為通過內部類實現多線程環境中的單例,
JVM內部機制能夠保證當一個類被載入時,這個類的載入過程是安全執行緒的,當我們第一次調用getInstance方法時,JVM能夠保證instance只被建立一次,並且保證初始化完成,這樣我們就不再需要擔心instance沒有被建立完成了,同時實現了惰性載入單例
Java 多線程編程中單例的實現