單例模式比較簡單,用于創建全域唯一的一個物件,這里直接貼出它的定義
單例模式保證一個類僅有一個實體,并提供一個訪問它的全域訪問點,
有什么用呢?很多物件只有一個,比如任務管理器,檔案,假如有好幾個物件,就會出現BUG,
那么,利用全域靜態變數實作單一物件如何呢?
- 全域靜態變數確實能夠保證全域訪問,但是無法保證全域只有一個物件
- 假如直接賦值給一個全域變數,就意味著要在一開始就初始化它,如果它很大,然而運行程序中又沒有使用到,就會導致空間的浪費,
單例模式的實作
將建構式訪問設計為private會怎么樣?如下代碼所示
public class Myclass{
private Myclass(){};
}
這個類能否被實體化呢?
很明顯做不到,因為它的建構式被設為private的,也就是說只有該類的實體才能呼叫它,然而不用建構式又無法實體化物件,
我們再加入一個靜態函式,用于呼叫這個建構式,
public class Myclass{
private Myclass(){}
public static Myclass getInstance(){
return new Myclass();
}
}
這樣就可以實體化該類了,再稍作修改,就可以得到一個只能被實體化一次的類,
懶漢式,執行緒不安全
// NOTE: This is not thread safe!
public class Singleton {
private static Singleton uniqueInstance;
//私有化的建構式,保證它不會被實體化
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
然而這么做并非執行緒安全的,當多個執行緒訪問getInstance()方法時,就可能會實體化出多個物件,
懶漢式,執行緒安全
一個粗暴的解決方法,我們給getInstance()方法加上synchronized關鍵字,
public class Singleton {
private static Singleton uniqueInstance;
//私有化的建構式,保證它不會被實體化
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
然而,實際上我們只需要在第一次呼叫getInstance()才需要同步,這樣加上同步后會引入不必要的效率降低,假如getInstance()方法需要被頻繁地呼叫,那么會大大降低效率,
餓漢式,執行緒安全
假如創建一個物件的消耗不大的話,我們可以考慮直接在初始化的時候實體化物件,如下所示
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
//私有化的建構式,保證它不會被實體化
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
這種在初始化時直接實體化單例的實作方式,我們叫它“餓漢式”,主要用于物件被頻繁創建并且創建消耗不大的情況,與之對應的前面的直到使用才創建單件的實作方式稱為“懶漢式”,
雙檢鎖/雙重校驗鎖
那么當單例創建消耗比較大,而且可能需要創建頻繁,也可能不會被創建,該怎么辦呢?用synchronized修飾方法會由于同步造成不必要的性能下降,用餓漢式創建方式又會讓初始化就實體化物件,而這個物件可能從未被實體化,
雙檢鎖/雙重校驗鎖可以幫助我們解決這個問題,我們可以先檢查物件是否被創建,如果沒有被創建,我們才進行同步,
//
// Danger! This implementation of Singleton not
// guaranteed to work prior to Java 5
//
public class Singleton {
private volatile static Singleton uniqueInstance;
// volatile確保當uniqueInstance被實體化,執行緒訪問它是執行緒安全的
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
// 假如單件為空,才會對整個類進行加鎖
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
很巧妙的方法,遺憾的是在1.4版本以前的Java版本中,不支持volatile修飾字,所以用的時候注意Java的版本,
單例模式的黑暗面
使用單例模式的類是無法被繼承的,因為它的建構式是private修飾的,無法被擴展,然而將privata改為protected又會破壞單例模式,別的類也可以實體化它了,假如一個設計大量使用了單價模式,那么很有可能是有問題的,因為一般情況下它的使用場合不多,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/244230.html
標籤:其他
上一篇:流密碼加密(RC4和LFSR)
