DesignPattern - Basic - Singleton

Mon, Aug 6, 2018 閱讀時間 2 分鐘

Singleton

確保在 java 虛擬機中只存在該類的一個實例。單例類必須提供一個全局訪問點來獲取類的實例。

  • 一個類應該只允許一個實例,
  • 應該允許全局訪問該單個實例。
  • 用於限制從其他類實例化類的私有構造函數。
  • 同一類的私有靜態變量,是該類的唯一實例。
  • 公共靜態方法,返回類的實例,這是外部世界獲取單例類實例的全局訪問點。

Reflection, Clone, 序列化/反序列化 違反 Singleton


   public static Singleton instance= new Singleton();
   
   private Singleton() {
   	System.out.println("creating instance.....");
   }
   
   public static Singleton getInstance() {
   	return instance;
   }



   public static void main(String[] args) throws Exception{
   	SingletonS s1 = SingletonS.getInstance();
   	SingletonS s2 = SingletonS.getInstance();
   	System.out.println("Hashcode of Object s1: " +s1.hashCode());
   	System.out.println("Hashcode of Object s2: " +s2.hashCode());


       // Reflection
       Class clazz = Class.forName("com.dev.dp.creational.singleton.SingletonR");
   	Constructor<SingletonR> ctr= clazz.getDeclaredConstructor();
   	ctr.setAccessible(true);
   	SingletonR s3 = ctr.newInstance();
   	System.out.println("Hashcode of Object s3: " +s3.hashCode());
   	
       // Clone
       Singleton s3 = (Singleton)s2.clone();
   	System.out.println("Hashcode of Object s3: " +s3.hashCode());

       // Serialization / Deserialization
   	ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/tmp/s2.ser"));
   	oos.writeObject(s2);
   	
   	ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/tmp/s2.ser"));
   	SingletonS s3= (SingletonS)ois.readObject();
   	
   	System.out.println("Hashcode of Object s3: " +s3.hashCode());

修復


    if(instance != null){
        throw new RuntimeException("無法創造新 instance");
    }
    return Singleton.getInstance();

多執行緒 eager / lazy init


    // 可加 volatile 關鍵字 , write the main memory, 可減少開銷
	private static SingletonT instance=null; //lazy initialization
	
	private SingletonT(){
		System.out.println("Creating...");
	}
	
    // double check lock
	public static SingletonT getInstance(){
		if (instance == null) {   //check1
			synchronized (SingletonT.class) {
				if (instance == null) {   //check2
					instance = new SingletonT();
				}
			}
		}
		return instance;
	}
	
	static void useSingleton(){
		SingletonT singleton = SingletonT.getInstance();
		System.out.println("Hashcode of Singleton Object: "+singleton.hashCode());
	}
	
	public static void main(String[] args) throws Exception {
		ExecutorService service = Executors.newFixedThreadPool(2);
		service.submit(SingletonT::useSingleton);
		service.submit(SingletonT::useSingleton);
		service.shutdown();
	}	
}

Singleton 目的

一次只能有一個實例(對象)的類是單例類。如果我們第二次嘗試實例化 Singleton 類,新變量也指向之前創建的實例。因此,無論我們通過任何實例對類內的任何變量進行任何修改,都會影響創建的單個實例的變量。例如,Runtime 類、Action Servlet、Service Locator 是一些 Singleton 類。私有構造函數和工廠方法也屬於單例類的例子。

創建 Singleton 類的主要目的是限制創建對象的數量。此外,通過使用 Singleton 類,每次發出新請求時都不會創建對象。相反,將重複使用單個對象。這就是 Java 中的 Singleton 模式主要用於多線程和數據庫應用程序的原因。例如,我們在創建數據庫連接時使用了單例類的概念。在這種情況下,我們通過不創建多個數據庫連接來限制內存浪費。

Note

  • 用private關鍵字聲明Singleton類的構造函數。通過將構造函數設為私有,沒有其他類可以從它實例化或生成對象。如果類中有多個構造函數,請將它們全部設為私有。
  • 同一類的私有靜態變量,它是該類的唯一實例。
  • 聲明一個返回類型為這個單例類的對象的靜態方法。