1. 程式人生 > >單例模式以及使用單例模式的時候的程式碼優化

單例模式以及使用單例模式的時候的程式碼優化

參考文獻:《Java程式效能優化》.葛一鳴

單例模式是最基礎的設計模式之一,它是一種物件建立模式,用於產生一個物件的具體例項,他可以保證系統中一個類只產生一個例項,在java語言中,這樣的行為能帶來兩大好處: (1)對於頻繁使用的物件,能夠省略物件建立的時間,對於那些重量級物件而言,是一筆非常可觀的系統開銷。 (2)因為new的次數減少因此對於系統記憶體的使用頻率也會降低,這會減輕GC(垃圾回收機制)的壓力,縮短GC停頓時間。 因此,對於系統的關鍵元件以及被頻繁使用的物件,使用單例模式,可以有效改善系統性能。

單例模式的參與者非常簡單,只有單例類和使用者兩個:

在這裡插入圖片描述

單例模式的核心在於通過一個藉口返回唯一的物件例項。 下面一個簡單的例子:

package com.example.demo;

/**
* @author 作者SMF:
* @version 建立時間:2018年9月23日 下午7:51:19
* 類說明
*/
public class Single {
	private  Single(){
		System.out.print("單例模式");
	}
	private static  Single instance = new Single();
	public static Single getInstance(){
		return instance;
	}
}

注意:單例類必須要有一個private級訪問的建構函式,只有這樣才能保證單例類不會再系統中的其它程式碼內被例項化,這點是很重要的,其次instance成員變數和gerInstance方法必須是static的。 這種實現方式非常簡單,而且十分可靠,唯一的不足之處是無法對instance做延時載入。假如單例的建立過程很慢,而由於instance成員變數是static定義的,JVM在載入單例類的時候,單例物件也會被建立,如果此時該單例類還在系統中扮演其他角色,那麼任何使用這個單例類的地方都會初始化這個單例變數,而不管是否會用到,比如單例類作為String工廠用於建立一些字串(該單例類及用於建立Single物件,又用於建立Sting物件:)

package com.example.demo;

/**
* @author 作者SMF:
* @version 建立時間:2018年9月23日 下午7:51:19
* 類說明
*/
public class Single {
	//構造方法
	private  Single(){
		System.out.println("單例模式");
	}
	//內部靜態變數
	private static  Single instance = new Single();
	//內部靜態方法
	public static Single getInstance(){
		return instance;
	}
	//內部靜態方法
	public static void  CreateString(){
		System.out.println("建立String");
	}
}

在直接執行Single.CreateString()方法的時候會輸出: 在這裡插入圖片描述

可以看到雖然沒有使用單例物件,但是他還是被創建出來了,解決方法是引入延時載入機制:

package com.example.demo;
/**
* @author 作者SMF:
* @version 建立時間:2018年9月25日 下午12:22:45
* 類說明
*/
public class LazySingleton {
	private LazySingleton(){
		System.out.println("單例建立");
	}
	private static LazySingleton instance = null;
	//懶載入機制
	public static synchronized LazySingleton getInstance(){
		if(instance == null){
			instance = new LazySingleton();
		}
		return instance;	
	}
}

但是上述程式碼中的懶載入加入了Synchronized關鍵字,同步的操作,因此線上程比較多的情況下,效能會降低許多,可以通過內部類的方式進一步優化:

package com.example.demo;
/**
* @author 作者SMF:
* @version 建立時間:2018年9月25日 下午1:34:03
* 類說明
*/
public class StaticSingleton {
	//使用內部類維護單例例項
	private static class Singleholder{
		private static StaticSingleton  instance = new StaticSingleton();
	}
	//獲取例項物件
	public static StaticSingleton getInstance(){
		System.out.println("獲取例項物件");
		return Singleholder.instance;
	}
	
	public static void createString(){
		System.out.println("建立String");
	}
}

這樣單獨使用CreateString()方法是不會載入單例類,只有使用了getInstance()方法的時候才建立單例類,而且多執行緒使用也不會造成大量的效能損耗。

另外還有特殊的序列化和反序列化的情況下會破壞單例,從而使系統中出現兩個單例類的例項,那麼解決方法是在單例類中增加一個 私有的readResolve()函式,可以防止生成新的單例物件,那麼就是一個比較完善的單例物件了。

private Object readResolve(){
		return instance;
	}