單例模式就是有且僅有一個實例化對象。這有什麼用處呢?其實有一些對象我們只需要一個,比如說:線程池、緩存、對話框、處理偏好設置和註冊表的對象、日誌對象、充當打印機顯卡等設備的驅動程序的對象。事實上,這些對象只能有一個實例,如果製造出了多個實例,就會導致許多問題產生,例如:程序的行爲異常、資源使用過量、或者是不一致的結果。
實現單例的方法:
1、首先將該類的構造方法私有化
2、然後提供一個static方法,供其他類獲得該類的實例化對象
3、在該類中初始化自己類的實例對象,或者在static方法中初始化
4、在其他類中調用該static方法即可以得到該類的實例
首先,我們先看一個單例模式的實現:
package com.xwj.singleton;
public class Singleton {
private static Singleton uniqueInstance;
//私有構造方法
private Singleton(){
}
//獲取單例實例
public static Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
在上面的代碼中,我們可以看到Singleton類中構造方法被私有化了,因此其他類根本不能通過new Singleton()方法創建Singleton的實例。只能通過 Singleton.getInstance()獲得Singleton的實例。而且從getInstance()方法中的代碼可以看出,每次當調用getInstance()只會返回同一個實例對象uniqueInstance,由此這樣就可以自始至終只有一個Singleton對象了。
現在我們對單例模式下定義:
單例模式:確保一個類只有一個實例,並提供一個全局訪問點。
我們已經初步認識了單例模式,但是單例模式還不是我們想象中的這麼簡單,在它裏面還會有一些問題產生。例如:現在有多個線程在併發訪問singleton.getInstance()方法,比如說線程1、線程2有可能同時進入到if(uniqueInstance == null)這個判斷條件體裏面,這樣線程1通過new Singleton()創建了一個Singleton對象返回了,然後線程2也通過new Singleton()創建了一個Singleton對象返回了。這樣就不是自始至終只有一個Singleton對象實例了。
處理多線程的問題,只要把getInstance()變成同步方法,多線程災難幾乎就可以輕易的解決了,例如:
package com.xwj.singleton;
public class Singleton {
private static Singleton uniqueInstance;
//私有構造方法
private Singleton(){
}
//獲取單例實例
public static synchronized Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
但是我們想只有第一次實例化Singleton對象時,才真正需要同步。換句話說,一旦設置好了uniqueInstance變量,就不需要同步這個方法了。之後每次調用這個方法同步都是一種累贅。
我們使用“雙重檢查加鎖”可以優化上面的代碼,同時也可以解決該多線程問題。如下:
package com.xwj.singleton;
public class Singleton {
private volatile static Singleton uniqueInstance;
//私有構造方法
private Singleton(){
}
//獲取單例實例
public static Singleton getInstance(){
if(uniqueInstance == null){
synchronized (Singleton.class) { //同步代碼塊
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
另外我們還有一種方法解決多線程併發導致創建多個Singleton對象的問題,不使用延遲實例化的做法,如下:
package com.xwj.singleton;
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
//私有構造方法
private Singleton(){
}
//獲取單例實例
public static Singleton getInstance(){
return uniqueInstance;
}
}
但是,還有一個問題存在是,我們使用這種方法,如果創建Singleton對象時非常消耗資源,而程序在這次的執行過程中又一直沒有用到它,不就形成浪費了嗎?如果存在這種情況,我們最好使用“雙重檢查加鎖”來解決多線程問題。
到此爲止,單例模式我就說這麼多了,現在對我們所學總結一下:
1、單例模式確保程序中的一個類最多隻有一個實例
2、單例模式也提供訪問這個實例的全局點
3、在java中實現單例模式需要私有的構造器、一個靜態方法和一個靜態變量
4、確定在性能和資源上的限制,然後小心的選擇適當的方法來實現單例,已解決多線程問題