1.爲什麼要用單例模式?
(1)對於頻繁使用的對象,可以省略對象創建時所消耗的時間。尤其是對重量級系統而言是一個非常可觀的系統開銷。
(2)由於new對象的次數變小,系統的內粗消耗也會降低,可以減輕gc的壓力,縮短gc停頓時間。
2.設計單例模式的幾種方法
(1)簡單的單例模式
public class Singleton {
private Singleton() {
System.out.println("Singleton is created");
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
這種設計單例模式的方法雖然簡單,但是無法對instance對象做延遲加載,因爲instance是static修飾的,所以當jvm加載類的時候就會常見instance對象,如果此時單例類中還有其他方法,在任何使用該單例類的時候就是加載instance對象,而不管instance方法是否被用到。
(2)懶加載及實現線程安全的單例模式
/**
* @author 江鵬飛
* 單例對象的延遲加載及線程安全實現
*/
public class LazySingleton {
private LazySingleton(){
System.out.println("lazySingleton is created!");
}
private static LazySingleton singleton = null;
public static synchronized LazySingleton getLazySingleton(){
if(singleton==null){
singleton = new LazySingleton();
}
return singleton;
}
}
這種方法解決了(1)中的延遲加載問題以及保證了線程安全(因爲在不同線程調用getLazySingleton()時在一個線程創建對象前,另外一個線程調用的時候會誤認爲當前instance爲空),這種方法雖然解決了上述問題,但是如果在代碼中使用synchronized時在多線程環境會出現線程阻塞影響系統效率。顯然這個也不是一個很好的實際方法。
(3)使用內部類的方式設計單例模式(推薦)
/**
* @author 江鵬飛
* 使用內部類的方式實現單例模式
*/
public class StaticSingleton {
private StaticSingleton(){
System.out.println("StaticSingleton is created!");
}
private static class SingletonHolder{
private static StaticSingleton instance = new StaticSingleton();
}
public static StaticSingleton getInstance(){
return SingletonHolder.instance;
}
}
這種方法解決了(2)中多線程環境下線程阻塞問題提高了系統的性能,使用內部類來維護單例的實例,當StaticSingleton 被加載時它的內部類並不會初始化。當StaticSingleton 被jvm加載時單例類SingletonHolder不會被初始化,而當調用 getInstance()方法時內部類纔會被jvm加載從而初始化instance,因爲實例對象的創建在類加載時完成,則天生對線程友好。getInstance()也不需要synchronize修飾。這實現方式同時具備以上兩種實現的優點。
注意: 通常情況下用上面設計的單例方法可以確保系統中只存在一個實例,但是當通過反射機制,強行調用單例類的私有構造函數生成多個單例。
(4)序列化/反序列化的單例實現
package com.ycit.singleton;
import java.io.Serializable;
/**
* @author 江鵬飛
* 序列化反序列化單例模式的實現
*/
public class SerSingleton implements Serializable {
String name;
private SerSingleton() {
System.out.println("Singleton is created");
name="SerSingleton";
}
private static SerSingleton instance = new SerSingleton();
public static SerSingleton getInstance() {
return instance;
}
private Object readResolve(){
return instance;
}
}