[英]Need of volatile keyword in case of DCL
我只是在實踐中閱讀並發性。 我知道有必要在字段的雙重檢查鎖定機制中使用volatile關鍵字,否則線程可以讀取非null對象的陳舊值。 因為不使用volatile關鍵字就可以對指令重新排序。 因此,在調用構造函數之前,可以將引用分配給資源變量。 因此線程可以看到部分構造的對象。
我對此有疑問。
我認為同步塊也限制了編譯器對指令的重新排序,所以為什么我們在這里需要volatile關鍵字?
public class DoubleCheckedLocking {
private static volatile Resource resource;
public static Resource getInstance() {
if (resource == null) {
synchronized (DoubleCheckedLocking.class) {
if (resource == null)
resource = new Resource();
}
}
return resource;
}
}
如果調用線程(T1)也從同步塊(在同一鎖上)讀取線程,則JMM僅保證線程T1將在同步塊內看到由另一個線程T2創建的正確初始化的對象。
由於T1可以看到資源不為null,因此無需經過同步塊就可以立即返回它,因此它可以獲取一個對象,但看不到其狀態正確初始化 。
使用volatile可以帶來保證,因為在volatile字段的寫入與該volatile字段的讀取之間存在事前發生的關系。
正如其他人所觀察到的那樣,在這種情況下必須具有可變性,因為在首次訪問資源時可能發生數據爭用。 在沒有volatile
,無法保證讀取非空值的線程A
實際上會訪問完全初始化的資源-如果同時在同步部分的線程B
中構建了該線程A,尚未達到。 然后,線程A
可以嘗試使用半初始化的副本。
從JSR-133(2004)開始使用時 ,仍然不建議使用volatile進行雙重檢查鎖定,因為它不易讀,並且不如推薦的替代方法有效 :
private static class LazyResourceHolder {
public static Resource resource = new Resource();
}
...
public static Resource getInstance() {
return LazyResourceHolder.something;
}
這是按需初始化Holder Class習慣用法,根據上一頁,
[...]它的線程安全性來自以下事實:保證屬於類初始化的一部分的操作(例如靜態初始化程序)對於使用該類的所有線程都是可見的;其惰性初始化是由於內部類的事實在某些線程引用其字段或方法之一之前不會加載。
實際上,這里不需要使用volatile
。 使用volatile
將意味着每次在線程方法中使用實例變量時都會有多個線程,這不會優化讀取的內存,但要確保一次又一次地讀取它。 我唯一使用volatile
是在線程中有停止指示符的地方( private volatile boolean stop = false;
)
像您的示例代碼中那樣創建單例會變得非常復雜,並且不會真正提高速度。 JIT編譯器非常擅長進行線程鎖定優化。
使用以下方法創建單例會更好:
public static synchronized Resource getInstance() {
if (resource == null) {
resource = new Resource();
}
return resource;
}
這更容易閱讀並推斷其對人類的邏輯。
另請參見您是否曾經在Java中使用volatile關鍵字? ,而volatile
實際上通常用於線程中的某些循環結束標志。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.