A5) Singleton(單態(tài)模式)
定義:保證一個(gè)類(lèi)只存在一個(gè)實(shí)例,并提供一個(gè)全局訪(fǎng)問(wèn)的指針。
這個(gè)也是非常常見(jiàn)的模式,可以說(shuō)很難在一個(gè)項目中不使用這個(gè)模式。對于全局變量來(lái)說(shuō),唯一和同步是至關(guān)重要的,一般來(lái)說(shuō)以下面的這種方式來(lái)實(shí)現。
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
第一行必須用private的構造函數,這樣才能防止別的class生成Singleton的實(shí)例,而第二行的static定義讓變量instance在所有Singleton實(shí)例中是唯一的(當然,這個(gè)例子實(shí)際上只有一個(gè)實(shí)例),那么每次通過(guò)getInstance()得到的變量instance就是Singleton的唯一實(shí)例。還有一種方法,如下:
public class Singleton {
private Singleton(){}
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
這種方法叫做lazy initialization技術(shù),不過(guò)關(guān)于這種技術(shù)的爭議很多,牽涉到double-checked locking(DCL)問(wèn)題。關(guān)于這個(gè)問(wèn)題,稍微解釋一下,具體我也不是太明確,有興趣可以另外研究。編譯器在編譯執行命令的時(shí)候,由于會(huì )使用代碼優(yōu)化,在synchronized段中的代碼就有可能會(huì )被提前執行以加快代碼效率。這個(gè)時(shí)候,也就是instance = new Singleton();這部分語(yǔ)句可能被預先優(yōu)化執行,那么它的唯一性也就不能保證了。雖然還只是在理論上推論這個(gè)結果,并沒(méi)有用事實(shí)來(lái)驗證這一說(shuō)法,現在也沒(méi)發(fā)現這個(gè)方法造成的不良后果,但是,我覺(jué)得還是推薦第一種方法,簡(jiǎn)單而容易理解。 注:感謝rwyx的指點(diǎn),因為以前理解有偏差,所以將上面部分刪除后重新改寫(xiě)。
這種方法叫做lazy initialization技術(shù),使用了synchronized關(guān)鍵字,不過(guò)這種方法的系統開(kāi)銷(xiāo)比較大,所以還是推薦地一種方法,簡(jiǎn)單而且效率高,最好在定義時(shí)追加final關(guān)鍵字。
關(guān)于Singleton,在C++/C#等語(yǔ)言中會(huì )用到一種叫做double-checked的技術(shù),目的是為了解決lazy initializtion技術(shù)中同步代碼效率低的問(wèn)題。具體實(shí)現如下:
public static Singleton getInstance() {
if (instance == null) {
synchronized {
if (instance == null) {instance = new Singleton();}
}
}
return instance;
}
即在同步前做一次判斷,這樣只有第一次時(shí)會(huì )進(jìn)入同步段,這樣效率也很高。不過(guò)這種方法在java中卻是錯誤的。因為java語(yǔ)言規范和C++/C# 等不同,是一個(gè)非常靈活的規范。java編譯器可以自由地重排變量的初始化和訪(fǎng)問(wèn)順序,以提高運行時(shí)效率;同時(shí)java中的變量訪(fǎng)問(wèn)是可以被自動(dòng)緩存到寄存器的,這也導致潛在的JIT 編譯器相關(guān)的依賴(lài)性錯誤。每個(gè)編譯器可能有不同的實(shí)現方法,同樣的代碼在不同編譯器和執行環(huán)境下可能有不同的表現。解決的辦法有很多,使用線(xiàn)程局部存儲(ThreadLocal)來(lái)保存instance變量,或者使用volatile關(guān)鍵字來(lái)定義instance,強制java編譯器不進(jìn)行優(yōu)化。DCL參考資料: http://www.blogcn.com/User8/flier_lu/blog/1620823.html
參考:
1、 http://www.jdon.com/designpatterns/singleton.htm(中文、java實(shí)例)
2、 http://www.dofactory.com/Patterns/PatternSingleton.aspx(英文、C#實(shí)例、UML)
3、 http://www.techscore.com/tech/DesignPattern/Singleton.html(日文、java實(shí)例、UML)推薦
聯(lián)系客服