2-單例模式 volatile 原子性(要麼都成功,要麼都失敗)

單例模式 volatile 原子性(要麼都成功,要麼都失敗)

 

單例模式,顧名思義就是隻有一個實例,可以分爲餓漢式和懶漢式以及JDK1.5以後引入的枚舉;

餓漢式

優點是當類在加載的時候就已經創建了實例,所以是安全的

缺點是當類加載的時候就已經創建並完成了實例化,沒有達到lazy-loading(延遲加載)的效果,所以如果該類始終沒有用到,就會造成內存的浪費

懶漢式是和懶漢式相反,這種方式實現了lazy-loading,但是有一個明顯的缺點就是,這種單例只能在單線程環境下使用,在多線程環境下,一個線程剛剛通過(null == instance)語句的同時,另一個線程也通過了該語句塊,那麼這個時候就會產生兩個實例,這樣就與單例模式只有一個實例的核心思想相悖了。所以在多線程模式不可使用該種方式。

最好的方式是雙層線程鎖(保障安全)+volatile(禁止重排序,保證了變量修改的可見性,但不保證原子性)

那麼有沒有一種方式可以在不使用lock、synchronized的方式下實現線程安全的單例模式呢?答案是,有的,那就是使用CAS。

  CAS是什麼呢?Compare And Swap,顧名思義就是比較和交換。CAS是項樂觀鎖技術,其包含三個參數,分別爲V(待更新的值)、E(期望值)、N(新值),當V和E不相同時,說明其他線程已經做過更新了,此時該線程不執行更新操作,或者再次嘗試讀取V值再次嘗試修改該值,也可以選擇放棄該操作。若是V和E相等,則當前線程可以修改V值,也就是執行CAS操作。CAS操作中沒有鎖的參與,但是針對其他線程針對共享資源的操作做了處理。由於CAS中沒有鎖的參與,所以針對線程共享資源的操作也不會發生死鎖了,可以說CAS天生免疫死鎖。

public class Singleton {

private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

private Singleton(){}

public static Singleton getInstance(){

for(;;){//不限次數的自旋循環,如果CAS一直失敗,CPU的執行開銷被消耗很嚴重

Singleton singleton = INSTANCE.get();

if(null != singleton){

return singleton;

}

singleton = new Singleton();

if(INSTANCE.compareAndSet(null, singleton)){//當前實例爲null,才替換當前實例爲singleton

return singleton;

}

}

}

}

優缺點分析:該方式使用CAS實現線程安全,實現相比傳統的鎖機制來說,CAS依靠的是底層硬件(CPU的CAS指令)來實現的,不需要進行頻繁的線程切換和阻塞而造成資源的額外消耗。

        但是這種方式還是有缺點的,CAS的自旋循環如果長時間不成功,則會給CPU帶來非常大的執行開銷。另外一點就是如果N個線程同時執行到singleton=new Singleton()的時候,則會同時創建大量的實例,很有可能發生OOM。

  CAS的缺點:首先CAS的ABA問題,這個可以通過添加版本號或時間戳來解決,在比較完內存中的值以後,再比較時間戳或者版本號是否一致。

        CAS的自旋操作,如果CAS長期不成功,會一直重試,會嚴重增加CPU的執行開銷。JDK1.6以後默認開啓了自旋(--XX:+UseSpinning),可以通過JVM設置CAS的自旋操作次數來解決(-XX:PreBlockSpin=10,JVM的默認自旋次數是10),當超過指定次數後,自動失敗退出。還有一種自適應自旋鎖,自旋的時間不再固定,會根據前一次同一個鎖上的自旋時間以及鎖的擁有者的狀態來決定的。

        CAS的功能的侷限性,CAS只能保證單個內存中的值的原子性,在java中原子性不一定能保證線程安全,還需要volatile保證有序性來實現線程安全。在需要保證多個內存中的值的情況下,CAS也無能爲力,可以看情況使用悲觀鎖。所以說在併發衝突概率比較高的環境中,儘量不要使用CAS。其次CAS的核心是依靠可以直接調用底層資源的Unsafe類的CompareAndSwap()方法實現的,在java使用只能使用Atomic包下的相關類,侷限性比較大。

參考地址

https://www.cnblogs.com/dcfwow/p/10770397.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章