volatile與單例模式

2021-09-26 22:50:54 字數 1543 閱讀 2835

參考文獻:volatile關鍵字的作用、原理

保持記憶體可見性:所有執行緒都能看到共享資料的最新值。

防止指令重排序

(1)讀取前先從記憶體重新整理最新的值。

(2)寫入後立即同步回記憶體中。

插入記憶體屏障來禁止重排序。

例如:下面是基於保守策略的jmm記憶體屏障插入策略:

在每個volatile寫操作的前面插入乙個storestore屏障。

在每個volatile寫操作的後面插入乙個storeload屏障。

在每個volatile讀操作的後面插入乙個loadload屏障。

在每個volatile讀操作的後面插入乙個loadstore屏障。

<1>為了實現單例模式,需要再取物件例項前用if(null)判斷是否存在。

class singleton 

public static singleton getinstance()

return instance;

}}

<2>這樣,多執行緒時也會出現多個例項。所以加同步**塊。

class singleton 

public static singleton getinstance()

}return instance;

}}

<3>加了同步**塊,序列化了,效率太低了。所以使用雙重檢查鎖。

class singleton 

public static singleton getinstance() }}

return instance;

}}

<4>指令重排序

instance = new singleton();

它並不是乙個原子操作。事實上,它可以」抽象「為下面幾條jvm指令:

memory = allocate();    //1:分配物件的記憶體空間

initinstance(memory); //2:初始化物件

上面操作2依賴於操作1,但是操作3並不依賴於操作2,所以jvm可以以「優化」為目的對它們進行重排序,經過重排序後如下:

memory = allocate();    //1:分配物件的記憶體空間

ctorinstance(memory); //2:初始化物件

可以看到指令重排之後,操作 3 排在了操作 2 之前,即引用instance指向記憶體memory時,這段嶄新的記憶體還沒有初始化——即,引用instance指向了乙個"被部分初始化的物件"。此時,如果另乙個執行緒呼叫getinstance方法,由於instance已經指向了一塊記憶體空間,從而if條件判為false,方法返回instance引用,使用者得到了沒有完成初始化的「半個」單例。

(一)設計模式之單例模式 volatile

簡單理解 在記憶體中某個例項物件只有乙個,並由程式程序中的其他執行緒共享該例項。1 了解物件建立過程 第一步 分配記憶體空間 第二步 呼叫建構函式,初始化例項。2 禁止指令重排序 當建構函式在物件初始化的完成之前就完成了物件賦值,在記憶體中開闢一片儲存區域後直接返回記憶體的引用,但是這個時候還沒正真...

單例模式 雙層檢驗鎖 volatile

package com.utils.threads public class dl return instance public static void main string args 分析 加volatile的必要性 原因在於指令重排的存在,加入volatile可以禁止指令重排 當某乙個執行緒執...

單例模式的雙重檢測鎖與volatile禁止重排

public class demo public static demo getinstance return instance 上面單例實現方式在單執行緒訪問下沒有問題,但是在併發訪問時,會產生多個物件。如程式啟動 a執行緒獲取instance執行完if判斷為null後,執行緒b獲取到cpu執行權...