#經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實例。 // 其他有用的實例變數寫在這裡 //構造器聲明為私有,只有Singleton可以實例化這個類! ...
經典的單件模式
public class Singleton {
private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實例。
// 其他有用的實例變數寫在這裡
//構造器聲明為私有,只有Singleton可以實例化這個類!
private Singleton() ()
public static Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();//getInstance()方法提供了一種實例化該類的方式,也返回它的一個實例。
}
return uniqueInstance ;
}
}
單件模式沒有公開的構造器,構造器聲明為私有;為了獲得一個單件對象,不是實例化一個,只是請求一個實例。因此類有一個靜態方法,稱為getInstance()。
用途:常常被用來管理資源池,像連接或者線程池。
你有一個包含註冊表設置的對象。你不想讓這個對象有多個副本,到處賦值一這會導致混亂。通過使用像單件模式這樣的對象,你可以確保應用中的每個對象使用同一全局資源。
例子--巧克力工廠
一個Choc-O-Hoic公司的工業強度巧克力鍋爐控制器類
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
//這段代碼只有在鍋爐空的時候才啟動!
public ChocolateBoiler(){
empty = true;
boiled = false;
}
//要往鍋爐里填充原料,鍋爐必須是空的。一旦鍋爐滿了,我們就設置empty和boiled標誌
public void fill() {
if(isEmpty()) {
empty = false;
boiled = false;
//往鍋爐里填充牛奶/巧克力混合物
}
}
//要排空鍋爐,鍋爐必須是滿的 (非空) 且煮過的。一旦排出完畢,我們把empty設置回true。
public void drain() {
if(!isEmpty() && isBoiled()) {
//排出煮沸的牛奶和巧克力
empty = true;
}
}
//要煮混合物,鍋爐必須是滿而且未煮過的。一旦煮沸,我們r把boiled標誌設為true。
public void boil() {
if(!isEmpty() &&!isBoiled()) {
//將爐內物煮沸
boiled = true;
}
}
public boolean isEmpty() {return empty;}
public boolean isBoiled() (return boiled;}
}
改成單件模式
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
//改動一:
private static Chocolateboiler uniqueinatance;
//改動二:
private ChocolateBoiler(){
empty = true;
boiled = false;
}
//改動三:
public static ChocolateBoiler getInstance(){
if(uniqueInstance == null) {
uniqueInstance = new ChocolateBoiler()
}
return uniqueInstance;
}
public void fill() {
if(isEmpty()) {
empty = false;
boiled = false;
//往鍋爐里填充牛奶/巧克力混合物
}
}
// 剩下的chocolateBoiler代碼·
}
單件模式
單件模式:確保一個類只有一個實例,並提供一個全局訪問點;
類圖:
添加線程造成的問題
解決多線程:通過把getInstance()方法變成同步(Synchronized)方法;
但是同步導致性能開銷大
改進多線程
1.如果getlnstance()的性能對應用來說不是很關鍵,什麼也不做。
2.轉為急切(eagerly) 創建實例,而不用延遲創建
用這個方法,當類被載入時,我們依靠JVM來創建單件的唯一實例。JVM 保證在任何線程訪問靜態uniqueInstance變數之前,實例會被創建。
public class Singleton {
//在靜態初始化器中創建單件實例,這段代碼保證是線程安全的!
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;//我們已經有了一個實例,因此返回它即可。
}
}
3.用“雙重檢查加鎖”在getlnstance()減少使用同步
雙重檢查加鎖不適用1.4以及更早版本的Java!
public class Singleton {
//volatile關鍵詞確保:當uniquelnstance變數被初始化為單件實例時,多個線程正確處理uniquelnstance變數
private volatilestatic Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if(uniqueInstance == null) {//檢查實例,如果沒有,進入同步區塊。
synchronized(Singleton.class){//註意,只有第一次才同步
if(uniqueInstance == null) {
uniqueInstance = new Singleton();//進入區塊後,再檢查一次。如果依然是空的,創建一個實例。
}
}
}
return uniqueInstance;
}
}
總結
單件,確保一個類只有一個實例,並提供全局訪問點。
當你需要確保應用中的某個類只有一個實例,就用單件模式吧。
要點
1.單件模式確保應用中個類最多只有一個實例。
2.單件模式也提供訪問此實例的全局點。
3.Java的單件實現用了一個私有構造器、一個靜態方法以及一個靜態變數。
4.檢查你的性能和資源約束,為多線程應用小心選擇一個適當的單件實現 (我們應該把所有應用都考慮為是多線程的)。
5.提防雙重檢查加鎖實現。Java 5之前的版本,不是線程安全的
6.如果你使用多個類載入器,要小心,可能導致單件實現失效,導致出現多個實例。
7.你可以使用Java的枚舉來簡化單件的實現。