單例模式,模式

來源:互聯網
上載者:User

單例模式,模式
1.優缺點

  單利模式就是在一個jvm中只能存在一個執行個體(不考慮反射)這樣設計主要有兩方面好處:

    1.從jvm來說,對於頻繁使用的對象,可以減去建立的時間(這對於重量級的對象,是非常客觀的開銷),由於new 對象的操作減少,對系統記憶體的使用頻率降低,將會減輕GC壓力,縮短GC停頓時間(摘自 java程式效能最佳化 --葛一鳴)。

    2.從設計來講,某些執行個體一個系統中本應只存在一個(邏輯上),並且只對同一對象操作,能有效保證一致性(並發時可相應處理)。

  同時也存在一些需要注意的問題:   

    1、由於單利模式中沒有抽象層,不利於擴充,所以很多責任都是自己扛,可能會導致單例類的職責過重,在一定程度上違背了“單一職責原則”。

    2.如這個執行個體建立過程很慢而且不一定會用到,可能需要消極式載入。

    3、濫用單例將帶來一些負面問題,如為了節省資源將資料庫連接池對象設計為的單例類,可能會導致共用串連池對象的程式過多而出現串連池溢出;如果執行個體化的對象長時間不被利用,系統會認為是垃圾而被回收,這將導致對象狀態的丟失。

2.實現方式

  實現單例的根本是私人化構造器(在類內部建立對象),然後根據不同的情境設計擷取執行個體的方法,下面是幾種常見的實現方式。

  1.餓漢式: 這種方式在類載入時就完成了初始化,所以類載入較慢,但擷取對象的速度快。 基於類載入機制實現可避免多線程的同步問題,但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化instance顯然沒有達到消極式載入的效果。  

1 public class Singleton1 {//惡漢式2     private Singleton1(){}3     private static Singleton1 singleton = new Singleton1();4     public static Singleton1 getInstance(){5         return  singleton;6     }7 }

  2.懶漢式:可實現消極式載入,但是多線程下存在致命問題。


 1 public class Singleton2 {//懶漢式 2     private Singleton2(){}; 3     private static Singleton2 singleton; 4     public static Singleton2 getInstance(){ 5         if(singleton == null){ 6             singleton = new Singleton2(); 7         } 8         return  singleton; 9     }10 }

  3.雙重檢查模式:改進的懶漢式。為解決線程同步問題,最簡單的方法是對getInstance方法整體加關鍵字synchronized,但是這種實現方式效率會至少低2個數量級。其中一種不錯的改進方式是雙重檢查模式(DCL),這種寫法在getSingleton方法中對singleton進行了兩次判空,第一次是為了不必要的同步,第二次是在singleton等於null的情況下才建立執行個體。在這裡用到了volatile關鍵字,在這裡使用volatile會或多或少的影響效能,但考慮到程式的正確性,犧牲這點效能還是值得的。DCL優點是資源使用率高,第一次執行getInstance時單例對象才被執行個體化,效率高。缺點是第一次載入時反應稍慢一些,在高並發環境下也有一定的缺陷(DCL失效),雖然發生的機率很小。

 1 public class Singleton3 {//雙重檢查模式(DCL) 2     private Singleton3(){}; 3     private static volatile Singleton3 singleton; 4     public static Singleton3 getInstance(){ 5         if(singleton == null){ 6             synchronized(Singleton3.class){ 7                 if(singleton == null){ 8                     singleton = new Singleton3(); 9                 }10             }11         }12         return  singleton;13     }14 }

  4.靜態內部類:既可以實現消極式載入,又不會有線程問題(推薦

1 public class Singleton4 {//靜態內部類模式(DCL)2     private Singleton4(){};3     static class ClassHolder{4         private static Singleton4 singleton = new Singleton4();5     }6     public static Singleton4 getInstance(){7         return  ClassHolder.singleton;8     }9 }

  5.枚舉:這種方式是Effective java作者Josh Bloch提倡的方式,它不僅能避免多線程的同步問題,而且還能防止還原序列化重新建立對象的問題。聽起來很給力,不過工作中基本見過,有機會試試。

1 public enum Singleton5 {//枚舉模式2     INSTANCE;3     public void whateverMethod(){4         //同枚舉類使用(它本來就是枚舉!),不需要擷取執行個體的方法。5     }6 }

  6.利用容器保證單例:在不考慮容器本身對並發的處理的情況下,這種方式能有效管理多種類型的單例,並且在使用時可以通過統一的介面進行擷取操作,降低了使用者的使用成本,也對使用者隱藏了具體實現,降低了耦合度。

 1 //利用容器實現單例 2 //最好將你要裝載的單例物件建構函數私人化,這樣可以避免很多問題 3 // 如果你能保證每次都是從這個類載入器擷取對象,建構函式是否私人化毫不相干 4 public class SingletonManager { 5     private static Map<String, Object> objMap = new HashMap<String,Object>(); 6     public static void registerService(String key, Object instance) { 7         if (!objMap.containsKey(key) ) { 8             objMap.put(key, instance) ; 9         }10 }11     public static Object ObjectGetService(String key) {12         return objMap.get(key) ;13     }14 }
 3.破壞單例的情況

  1.反射:就當他不存在吧。

  2.多個類載入器:例如一些servlet容器對每個servlet使用完全不同的類裝載器,這樣的話如果有兩個servlet訪問一個單例類,它們就都會有各自的執行個體。解決方案就是想辦法使用同一個類載入器(廢話)。

  3.序列化複原:當你序列化複原一個單例對象時候,就會出現多個單例對象。如這樣:

 1 public class Test { 2     public static void main(String[] args) throws IOException, ClassNotFoundException { 3         Singleton1 singleton = Singleton1.getInstance(); 4         //先將單例對象序列化到檔案 5         FileOutputStream outputStream = new FileOutputStream("E:Singleton.txt"); 6         ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); 7         objectOutputStream.writeObject(singleton); 8         objectOutputStream.flush(); 9         objectOutputStream.close();10         //從檔案讀取對象11         FileInputStream inputStream = new FileInputStream("E:Singleton.txt");12         ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);13         Singleton1 newSingleton = (Singleton1) objectInputStream.readObject();14 15         //test16         System.out.println(singleton == newSingleton); // false17         System.out.println(singleton == Singleton1.getInstance());// true18     }19 20 }

  有效方法是在單例類中加入readResolve方法(序列化操作提供了一個很特別的鉤子(hook)類中具有一個私人的被執行個體化的方法readresolve(),這個方法可以確保類的開發人員在序列化將會返回怎樣的object上具有發言權)。例如:

 1 public class Singleton1 implements Serializable{//惡漢式 2     private Singleton1(){} 3     private static Singleton1 singleton = new Singleton1(); 4     public static Singleton1 getInstance(){ 5         return  singleton; 6     } 7     private Object readResolve(){ 8         return singleton; 9     }10 }

  在運行上面的測試就會的到兩個true。

4.感謝:很多內容是從網上檢索整理得,感謝感謝!!

 

 

  

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.