單例模式:確保一個類只有一個實例,並提供一個全局訪問點。
什麼情況下需要單例模式?
一些類提供公共功能供別人調用,本身不會處理業務邏輯類會被許多類和線程調用
單例模式的一些注意點:
單例的生存期超長,會導致內存的持續佔用。
單例在多線程環境需要小心的處理線程互斥,進行資源保護。
單例在類的繼承樹中不利於使用,會破壞繼承體系。
單例的全局可見性帶來的設計破壞。
經典代碼示例以及改進:
public class Singleton {
private static Singleton uniqueInstance ;
public static Singleton getInstance() {
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
private Singleton() {
}
}
上面的代碼實現了延遲加載(假如一個單例類需要耗費好多資源時候延遲加載就有很大好處)但是這種做法在多線程的時候會出現問題,比如有兩個線程同時調用getInstance(),這時會new兩個對象。把getInstance()方法變成同步(synchronized)方法:
public class Singleton {
private static Singleton uniqueInstance ;
public static synchronized Singleton getInstance() {
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
private Singleton() {
}
}
但是同步會降低性能。
綜合考慮有以下幾種解決方案:
1、如果getInstance()的性能對程序不是很關鍵,那就不用考慮太多。
2、不用延遲實例化的方法。
public class Singleton {
private static Singleton uniqueInstance = new Singleton();//在靜態初始化器中創建單例,這段代碼保證了線程安全
public static Singleton getInstance() {
return uniqueInstance;
}
private Singleton() {
}
}
3、雙重檢查加鎖,在getInstance()中減少使用同步。
public class Singleton {
private volatile static Singleton uniqueInstance ;
public static Singleton getInstance() {
if (uniqueInstance == null){//A
synchronized (Singleton.class){
if (uniqueInstance == null){//C
uniqueInstance = new Singleton();//B
}
}
}
return uniqueInstance;
}
private Singleton() {
}
}
爲什麼需要兩次判斷爲null?其實這個意義在於防止多個線程同時進入第一個if內,比如說線程A執行到A行,線程B執行到B行,線程B還沒有返回。當線程A執行到C行,這時線程B初始化實例完畢,如果沒有裏面的再一次判斷就會生成兩個實例!
其他需要注意點事項:
1、單例模式和靜態類(把所有的方法和變量都定義成靜態的)的區別
2、如果程序中存在多個類加載器,則不同的類加載器有可能加載同一個類,這種情況下就會出現多個單例類實例並存。這種情況下則需要自行指定類加載器,並指定同一個類加載器。