線程安全的單例模式 雙重效驗鎖
1.單例模式:確保一個類只有一個實例,自行實例化並向系統提供這個實例(舉例 例如有三個線程 使用靜態方法,讓所創建出來的對象名來調取每一個線程。)
2.單例模式分類:餓單例模式(類加載時實例化一個對象給自己的引用),懶單例模式(調用取得實例的方法如getInstance時才會實例化對象)(java中餓單例模式性能優於懶單例模式,c++中一般使用懶單例模式)
3.單例模式要素:
私有構造方法
私有靜態引用指向自己實例
以自己實例為返回值的公有靜態方法
餓單例模式:
public static HungrySingleton getInstance {
private static HungrySingleton sc = new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return sc;
}
}
在程序執行開始的時候,餓單例模式就會new 出新的對象;這種寫法就是所謂的飢餓模式,每個對象在沒有使用之前就已經初始化了。這就可能帶來潛在的性能問題:如果這個對象很大呢?沒有使用這個對象之前,就把它加載到了內存中去是一種巨大的浪費。
懶單例模式
public class Singleton {
private Singleton(){
}
private static Singleton sc;
public static synchronized Singleton getInstance(){ //多線程時注意線程安全
if(sc == null){
sc= new Singleton();
}
return sc;
}
}
在程序執行開始的時候,只有在你需要用的時候才會new新對象 意思為空的時候才會new出新對象
雙重效驗鎖
public static SingleClass getInstance(){
if(sc==null){ //第一次校驗
synchronized (SingleClass.class){ //同步代碼塊
if(sc==null){ //第二次校驗
sc=new SingleClass(); //創建實例
}
}
}
return sc;
}
此寫法保證了,當多個進程進入第一個判斷鎖時,會被同步機制隔離,只有一個程序進入新建對象,再其他線程進入時,sc已經不為null,因此不會新建多個對象。這種方法就叫做雙重檢查鎖,但是也有一個問題,就是java是實行無序寫入的機制,在某個線程執行//2代碼的過程中,sc被賦予了地址,但是singleton對象還沒構造完成時,如果有線程訪問了代碼//1此時判斷sc不為空,但是方法返回的是一個不完整對象的引用。此時可能會產生錯誤!