java設計模式——單例模式

單例模式的使用動機java

  某些系統中的某些類的對象只須要一個, 或者只能是一個, 若是多於一個甚至會出現錯誤,這時候咱們就要用到單例模式。日誌對象, 打印池對象, 序列ID生成器等應用。單例模式可讓系統在減小內存空間的狀況下仍然能正常工做。安全

  生成單例的方式:1.懶漢式(線程不安全);2.懶漢式(線程安全);3.餓漢式;4.靜態內部類;函數

  5.雙重檢驗鎖;6.枚舉優化

這裏只介紹下:雙重檢驗鎖。貌似如今都這個比較多。spa

其餘單例模式能夠參考:線程

https://www.zybuluo.com/pastqing/note/164787日誌

http://cantellow.iteye.com/blog/838473code

雙重校驗鎖
orm

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

爲何要判斷兩次singleton對象是否爲空, 緣由仍是爲了不多個對象實例的生成。假如線程A拿到鎖進入同步塊生成了一個對象並返回。此時線程B拿到鎖進入同步塊,若是此時不判斷singleton的話,就會屢次建立對象, 形成單例失敗。  volatile關鍵字,確保進程之間獲取到的singleton是最新的。對象

        若是不在聲明 Singleton singleton 加volatile,那麼對於singleton = new Singleton不是一個原子操做。此時的雙重鎖的方式是有問題的:

    在執行new時JVM會發生如下幾個動做:

    1.給singleton分配內存

    2.調用構造函數, 初始化成員變量

    3.將singleton對象指向所分配的內存空間 當第三步完成後,對象就再也不是null了。可是JVM會進行指令的優化重排序, 將本來的1-2-3的順序可能變爲1-3-2。這樣就有可能在線程A實例化一個對象以後,線程B在3執行完後就搶佔了,此時實例已經非Null此時線程二就會返回該實例, 這樣就會出錯了。 

    在JDK1.5以後,可使用volatile解決以上問題。

    關鍵字volatile能夠說是java虛擬機提供的最輕量級的同步機制,它包含了兩方面的語義:

    1、當一個變量定義爲volatile時,它保證了此變量對全部線程的可見性。這裏的可見性是指當一條線程修改了這個變量的值,新值對於其餘線程來講是能夠當即知道的。而普通變量沒法作到這一點。普通變量的值在線程間傳遞須要經過主內存來完成。

例如,線程A修改了一個普通變量的值,而後向主內存中回寫,線程B等線程A回寫完成後再次主內存進行讀取時,新值纔會可見。

2、禁止指令重排優化。普通變量僅僅會保證在該方法的執行過程當中全部依賴賦值結果的地方都能保證取到正確的結果,而不能保證變量賦值的操做順序與代碼一致。 

上面使用volatile來解決雙重鎖實現帶來的問題, 正是運用了volatile的第二個語義。

相關文章
相關標籤/搜索