23種java設計模式之單例模式

背景:

  • 最近在看java設計模式,看了幾篇關於“單例模式”的文章,決定自己實踐一遍整理一下記錄下來。

定義:

  • 在它的核心結構中只包含一個被稱爲單例的特殊類。通過單例模式可以保證系統中一個類只有一個實例。

特點:

  • 單例類只能有一個實例;
  • 單例類必須自己創建自己的唯一實例;
  • 單例類必須給其它對象提供這一實例。

要點:

  1. 私有構造方法;
  2. 指向自己實例的私有靜態引用;
  3. 以自己實例爲返回值的靜態的公有方法。

優點:

  • 只存在一個實例,減少了內存的開支,特別是一個對象需要頻繁創建銷毀時,而且創建銷毀時性能又無法優化,單例模式的優勢就非常明顯;
  • 由於單例模式只存在一個實例,所以減少了系統的性能開銷,當一個單例對象產生時如果需要比較多的資源時,則可以在應用啟動時 * 直接生產一個單例,然後永駐內存的方式來解決;
  • 單例模式可以避免對資源的多重佔用,例如一個寫文件的操作,由於只存在一個實例在內存中,避免對同一個資源文件的同時寫操作;
  • 單例模式可以在系統設置全局的訪問點,優化和共享資源訪問,例如,可以設計一個單列類,負責所有數據表的映射處理。

缺點:

  • 單例模式一般沒有接口,擴展很困難,若要擴展,除了修改代碼基本上沒有第二種途徑可以實現;
  • 隱式使用引起類結構不清晰;
  • 單例對象如果持有Context,那麼很容易引起內存洩漏,此時需要傳給單例對象的Context最好是Application Context。

場景:

  • 需要頻繁實例化然後銷燬的對象;
  • 創建對象時耗時過多或者資源過多,但又經常用到的對象;
  • 資源共享的情況下,避免由於資源操作時導致的性能損耗;
  • 控制資源的情況下,方便資源之間的互相通信。

注意事項:

  • 只能使用單例類提供的方法獲得單例對象,不能使用反射,否則會實例化出新對象;
  • 不要做斷開單例類對象與類中靜態引用的危險操作;
  • 多線程使用單例共享資源時,注意線程安全問題。

代碼示範(單例類SingletonDemo):

  1. 第1種:懶漢式(線程不安全)
    1. 代碼:

      	private static SingletonDemo instance;
      
      	private SingletonDemo() {
      
      	}
      
      	public static SingletonDemo getInstance() {
      		if (null == instance) {
      			instance = new SingletonDemo();
      		}
      		return instance;
      	}
      
    2. 優缺點:
      1. 優點:在用戶第一次調用時纔去初始化,節約資源,起到了懶加載的作用。
      2. 缺點:第一次調用時稍慢,因爲要初始化,多線程不能正常工作。
  2. 第2種:懶漢(線程安全)
    1. 代碼:

      	private static SingletonDemo instance;
      
      	private SingletonDemo() {
      	}
      
      	public static synchronized SingletonDemo getInstance() {
      		if (null == instance) {
      			instance = new SingletonDemo();
      		}
      		return instance;
      	}
      

    2. 優缺點:
      1. 優點:在多線程中能很好的工作。
      2. 缺點:每次調用getInstance()時都需要進行同步,大部分時候都是用不到同步的,造成了資源的浪費,所以不建議使用。
  3. 第3種:餓漢式(天生的線程安全)
    1. 代碼:

      	private static SingletonDemo instance = new SingletonDemo();
      
      	private SingletonDemo() {
      	}
      
      	public static SingletonDemo getInstance() {
      		return instance;
      	}
      

    2. 優缺點:
      1. 優點:餓漢式在創建類的同時就創建了靜態實例供對象系統調用,以後不再發生改變, 所以是線程安全的。
      2. 缺點:
        1. 類加載時就完成了初始化,沒有達到懶加載的效果;
        2. 在類加載時就初始化了實例,如果想在創建實例前調用某個方法給它傳遞一個參數就變不到了。
  4. 第4種:DCL(雙重檢查模式)
    1. 代碼:

      	private volatile static SingletonDemo instance;
      
      	private SingletonDemo() {
      	}
      
      	public static SingletonDemo getInstance() {
      		if (null == instance) {
      			synchronized (SingletonDemo.class) {
      			}
      			if (null == instance) {
      				instance = new SingletonDemo();
      			}
      		}
      		return instance;
      	}    
    2. 實現原理:
  • 在getInstance()時對instance時進行了2次判空,第一次是爲了不必要的同步,第二次是爲了instance爲null * 才創建實例。
  1. 優缺點:
    1. 優點:DCL的優點是資源利用率高,第一次執行getInstance()時實例對象才被實例化,效率高。
    2. 缺點:第一次加載時反應稍慢一點,而且有失效的可能。
第5種:靜態內部類
  1. 代碼:

    	private static class SingletonHolder {
    		private static final SingletonDemo INSTANCE = new SingletonDemo();
    	}
    
    	private SingletonDemo() {
    	}
    
    	public static final SingletonDemo getInstance() {
    		return SingletonHolder.INSTANCE;
    	}
    

  2. 實現原理:
  • 第一次加載Singleton類時並不會初始化instance,只有第一次調用getInstance()方法時 * 虛擬機加載SingleHolder並初始化instance,這樣不僅能確保線程安全,也能保證實例的唯一性,很合理的單例模式。
第6種:枚舉類SingletonEnum
  1. 代碼:

    	INSTANCE;
    	public void whateverMethon() {
    		
    	}
    

  2. 優缺點:
    1. 優點:避免了線程安全,防止反序列化重新創建新的對象。
    2. 缺點:失去了類的一些特性,沒有延時加載。
第7種:註冊登記式
  1. 這個沒看明白,不知道是我閱讀的資料有問題還是自己理解力差。希望看到的小夥伴能給我講一下,最好有可運行的代碼示例。感激涕零!!!

總結:

  • 以上是閱讀了幾篇文章之後自己嘗試寫了幾個小例子,畢竟“紙上得來終覺淺,絕知此事要躬行”嘛。

參閱資料,非常感激:

  1. https://blog.csdn.net/itachi85/article/details/50510124
  2. https://blog.csdn.net/coder_pig/article/details/54411894
  3. https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247483786&idx=1&sn=072991244ec2f259c7049bc80317a728&scene=21#wechat_redirect


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章