單例模式以及使用單例模式的時候的程式碼優化
參考文獻:《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;
}