天天看點

重走Java設計模式——單例模式(Singleton Pattern)

前言

我們在開發當中使用最多的就是單例模式,那麼什麼是單例模式,下面我給大家分享我了解的單例模式。

單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬于建立型模式,它提供了一種建立對象的最佳方式。

這種模式涉及到一個單一的類,該類負責建立自己的對象,同時確定隻有單個對象被建立。這個類提供了一種通路其唯一的對象的方式,可以直接通路,不需要執行個體化該類的對象。

1、單例類隻能有一個執行個體。

2、單例類必須自己建立自己的唯一執行個體。

3、單例類必須給所有其他對象提供這一執行個體。

單例模式結構圖

重走Java設計模式——單例模式(Singleton Pattern)

單例模式實作方式

1.餓漢式

代碼執行個體:

public class Singleton { 
 
     private static Singleton instance = new Singleton(); 
      
     private Singleton (){}

     public static Singleton getInstance() {  
	     return instance;  
     }  
 }  
           

這種方式在類加載時就完成了初始化,是以類加載較慢,但擷取對象的速度快。 這種方式基于類加載機制避免了多線程的同步問題,但是也不能确定有其他的方式(或者其他的靜态方法)導緻類裝載,這時候初始化instance顯然沒有達到懶加載的效果。

2.懶漢式

2.1懶漢式(線程不安全)

代碼執行個體:

public class Singleton {  
      private static Singleton instance; 
       
      private Singleton (){}  
       
      public static Singleton getInstance() {  
	      if (instance == null) {  
          instance = new Singleton();  
	      }  
	      return instance;  
      }  
 }  
           

懶漢模式申明了一個靜态對象,在使用者第一次調用時初始化,雖然節約了資源,但第一次加載時需要執行個體化,反映稍慢一些,而且在多線程不能正常工作。

2.2懶漢式(線程安全)

代碼執行個體:

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

這種寫法能夠在多線程中很好的工作,但是每次調用getInstance方法時都需要進行同步,造成不必要的同步開銷,而且大部分時候我們是用不到同步的,是以不建議用這種模式。

3.雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)

代碼執行個體:

public class Singleton {  
    private volatile static Singleton singleton;
      
    private Singleton (){}  
    
    public static Singleton getSingleton() {  
	    if (singleton == null) {  
	        synchronized (Singleton.class) {  
		        if (singleton == null) {  
		            singleton = new Singleton();  
		        }  
	        }  
	    }  
	    return singleton;  
    }  
}  
           

這種方式采用雙鎖機制,安全且在多線程情況下能保持高性能。getInstance() 的性能對應用程式很關鍵。

4.登記式/靜态内部類

代碼執行個體:

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

這種方式能達到雙檢鎖方式一樣的功效,但實作更簡單。對靜态域使用延遲初始化,應使用這種方式而不是雙檢鎖方式。這種方式隻适用于靜态域的情況,雙檢鎖方式可在執行個體域需要延遲初始化時使用。

5.枚舉

代碼執行個體:

public enum Singleton {  
	    INSTANCE;  
    public void whateverMethod() {}  
}  
           

預設枚舉執行個體的建立是線程安全的,并且在任何情況下都是單例,上述講的幾種單例模式實作中,有一種情況下他們會重新建立對象,那就是反序列化,将一個單例執行個體對象寫到磁盤再讀回來,進而獲得了一個執行個體。反序列化操作提供了readResolve方法,這個方法可以讓開發人員控制對象的反序列化。在上述的幾個方法示例中如果要杜絕單例對象被反序列化是重新生成對象,就必須加入如下方法:

private Object readResolve() throws ObjectStreamException{
		return singleton;
}
           

6.使用容器實作單例模式

代碼執行個體:

public class SingletonManager { 
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() { }
  
  public static void registerService(String key, Objectinstance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;
  }
}
           

用SingletonManager 将多種的單例類統一管理,在使用時根據key擷取對象對應類型的對象。這種方式使得我們可以管理多種類型的單例,并且在使用時可以通過統一的接口進行擷取操作,降低了使用者的使用成本,也對使用者隐藏了具體實作,降低了耦合度。

單例模式的優缺點

優點

1、在記憶體裡隻有一個執行個體,減少了記憶體的開銷,尤其是頻繁的建立和銷毀執行個體(比如管理學院首頁頁面緩存)。

2、避免對資源的多重占用(比如寫檔案操作)。

缺點

沒有接口,不能繼承,與單一職責原則沖突,一個類應該隻關心内部邏輯,而不關心外面怎麼樣來執行個體化。

使用單例模式的注意事項

getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成 instance 被多次執行個體化。