單例模式應(yīng)用于一個類只有一個實例的情況,并且為其實例提供一個全局的訪問點。
特點:
1.一個類只有一個實例
2.自己創(chuàng)建這個實例
3.整個系統(tǒng)只能用這個實例
應(yīng)用場景
外部資源:每臺計算機有若干個打印機,但只能有一個PrinterSpooler,以避免兩個打印作業(yè)同時輸出到打印機。
內(nèi)部資源:大多數(shù)軟件都有一個(或多個)屬性文件存放系統(tǒng)配置,這樣的系統(tǒng)應(yīng)該有一個對象管理這些屬性文件。
實現(xiàn)方式
1.餓漢式:單例實例在類裝載時就構(gòu)建,急切初始化。(預(yù)先加載法)
/**
* 餓漢式(推薦)
*
*/
public class Singleton1 {
private Singleton1() {
}
public static Singleton1 instance = new Singleton1();
public Singleton1 getInstance() {
return instance;
}
}
優(yōu)點 |
1.線程安全
2.在類加載的同時已經(jīng)創(chuàng)建好一個靜態(tài)對象,調(diào)用時反應(yīng)速度快 |
缺點 |
資源效率不高,可能getInstance()永遠不會執(zhí)行到,但執(zhí)行該類的其他靜態(tài)方法或者加載了該類(class.forName),那么這個實例仍然初始化 |
2.懶漢式:單例實例在第一次被使用時構(gòu)建,延遲初始化。
class Singleton2 {
private Singleton2() {
}
public static Singleton2 instance = null;
public static Singleton2 getInstance() {
if (instance == null) {
//多個線程判斷instance都為null時,在執(zhí)行new操作時多線程會出現(xiàn)重復(fù)情況
instance = new Singleton2();
}
return instance;
}
}
懶漢式在單個線程中沒有問題,但在多線程就可能會出現(xiàn)兩個或多個Singleton2實例情況,
雖然后面實例化的Singleton2會覆蓋前面實例化的Singleton2,但最好避免這樣的情況。
改進方式就是加鎖synchornized
class Singleton3 {
private Singleton3() {
}
public static Singleton3 instance = null;
public static synchronized Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
優(yōu)點 |
資源利用率高,不執(zhí)行g(shù)etInstance()就不會被實例,可以執(zhí)行該類的其他靜態(tài)方法 |
缺點 |
第一次加載時不夠快,多線程使用不必要的同步開銷大 |
3.雙重檢測
class Singleton4 {
private Singleton4() {
}
public static Singleton4 instance = null;
public static Singleton4 getInstance() {
if (instance == null) {
synchronized (Singleton4.class) {
if (instance == null) {
instance = new Singleton4();
}
}
}
return instance;
}
}
優(yōu)點 |
資源利用率高,不執(zhí)行g(shù)etInstance()就不被實例,可以執(zhí)行該類其他靜態(tài)方法 |
缺點 |
第一次加載時反應(yīng)不快,由于java內(nèi)存模型一些原因偶爾失敗 |
4.靜態(tài)內(nèi)部類
class Singleton5 {
private Singleton5() {
}
private static class SingletonHelp {
static Singleton5 instance = new Singleton5();
}
public static Singleton5 getInstance() {
return SingletonHelp.instance;
}
}
優(yōu)點 |
資源利用率高,不執(zhí)行g(shù)etInstance()不被實例,可以執(zhí)行該類其他靜態(tài)方法 |
缺點 |
第一次加載時反應(yīng)不夠快 |
總結(jié):一般采用餓漢式(1),若對資源十分在意可以采用靜態(tài)內(nèi)部類(4),不建議采用懶漢式及雙重檢測(2、3)
另外
對于第二種可以采用volatile方式
volatile用更低的代價代替同步
解釋:同步的代價主要有覆蓋范圍決定,如果可以降低同步的覆蓋范圍,可大幅提升性能。
而volatile覆蓋范圍是變量級別的,因此同步代價很低。
volatile原理:告訴處理器,不要將其放入工作內(nèi)存,而是直接在主存操作。因此,當多處理器或多線程在訪問該變量時
都將直接操作主存,這在本質(zhì)上做到了變量共享。
volation優(yōu)勢:
1.更大的程度吞吐量
2.更少的代碼實現(xiàn)多線程
3.程序伸縮性好
4.比較好理解,無需太高的學(xué)習成本
volatile不足:
1.容易出問題
2.比較難設(shè)計
參考文獻
http://yiminghe.iteye.com/blog/404334
http://developer.51cto.com/art/201103/249322.htm