java單例大傢都熟悉,下面總結幾個簡單例子
首先是經典單例模式:
[java]
/*
* 經典單例模式
*/
public class SimpleInstance1 {
private static SimpleInstance1 instance = null;
private SimpleInstance1(){};
public static SimpleInstance1 getInstance(){
if (instance == null){
return new SimpleInstance1();
}
else return instance;
}
}
這種單例模式是在第一次調用getInstance的時候,對類進行初始化,以後調用時直接返回第一次初始化的實例。
但是這種模式不保證線程安全。例如,線程A和線程B同時第一次運行到如下語句
[java]
if (instance == null){
此時2個線程的判斷都為true,所以就都進行瞭一次實例化new SimpleInstance1()。
為瞭避免這種線程不安全問題,可以使用餓漢式,如下:
[java]
/*
* 餓漢一步到位方式
*/
public class SimpleInstance2 {
private static final SimpleInstance2 instance = new SimpleInstance2();
private SimpleInstance2(){};
public static SimpleInstance2 getInstance(){
return instance;
}
}
上面把初始化放到瞭static中,這樣在類裝載的時候就進行瞭初始化,確實可以保證線程安全(因為真正開始調用getInstance()之前就已經初始化瞭),但是卻丟失瞭部分系統資源。而且有一個明顯的缺點,就是new SimpleInstance2()中,不能根據程序運行情況動態的往構造方法中傳入參數。
現在我們回到最初的經典單例模式,為保證線程安全,我們做如下改進:
[java]
public class SimpleInstance3 {
private static SimpleInstance3 instance = null;
private SimpleInstance3(){};
public static synchronized SimpleInstance3 getInstance(){
if (instance == null){
return new SimpleInstance3();
}
else return instance;
}
}
我們在getInstance方法中加入synchronized關鍵字,保證同一時刻隻有一個線程可以進入此方法。這樣確實可以保證線程安全,而且也是在真正需要用到此類的時候進行第一次初始化的,但是這樣進行線程同步,性能開銷很客觀。
其實做出線程同步的目的,是防止首次初始化的時候出現線程不安全的情況,而上面的模式中,不僅首次初始化時要線程同步,即使初始化之後調用getInstance也進行線程同步限制瞭,不僅沒必要,還浪費瞭時間。針對這種情況進行如下的改進:
[java]
/*
* 雙重鎖定模式
*/
public class SimpleInstance4 {
private volatile static SimpleInstance4 instance = null;
private SimpleInstance4(){};
public static SimpleInstance4 getInstance(){
if (instance == null){
synchronized (SimpleInstance4.class) {
if (instance == null){
return new SimpleInstance4();
}
}
}
return instance; www.aiwalls.com
}
}
這個不多解釋瞭,隻在初始化的那部分代碼中加入線程同步,相比上面的那種同步小有改進。不過instance也被定義為volatile,因為new操作在jdk1.5以前不是一個規范安全的過程(具體可以參看jvm方面的資料),這樣也消耗瞭不少系統時間。
作者:laizhenhai88