首先要說的是,個人推薦使用餓漢模式和靜態內部類方式實現單例模式。其實,靜態內部類裏也是一個餓漢模式。
懶漢模式中,雙重檢查鎖定代碼如下:
public class Singleton{
// 靜態屬性,volatile保證可見性和禁止指令重排序
private volatile static Singleton instance = null;
// 私有化構造器
private Singleton(){}
public static Singleton getInstance(){
// 第一重檢查鎖定
if(instance==null){
// 同步鎖定代碼塊
synchronized(Singleton.class){
// 第二重檢查鎖定
if(instance==null){
// 注意:非原子操作
instance=new Singleton();
}
}
}
return instance;
}
}
volatile作用:以下會涉及到Java內存模型的知識
禁止指令重排序。我們知道new Singleton()是一個非原子操作,編譯器可能會重排序【構造函數可能在整個對象初始化完成前執行完畢,即賦值操作(只是在內存中開闢一片存儲區域後直接返回內存的引用)在初始化對象前完成】。而線程B在線程A賦值完時判斷instance就不爲null了,此時B拿到的將是一個沒有初始化完成的半成品。
保證可見性。線程A在自己的工作線程內創建了實例,但此時還未同步到主存中;此時線程B在主存中判斷instance還是null,那麼線程B又將在自己的工作線程中創建一個實例,這樣就創建了多個實例。
順便提一下,volatile禁止指令重排序只能保證volatile修飾的代碼之後的代碼不會在它之前執行。
轉自:https://blog.csdn.net/zcl_love_wx/article/details/80758162