3 回答

TA貢獻1848條經驗 獲得超2個贊
不,沒有傳遞關系。
JMM 背后的想法是定義 JVM 必須遵守的規則。如果 JVM 遵循這些規則,它們就有權根據需要重新排序和執行代碼。
在您的示例中,第 2 次讀取和第 3 次讀取不相關 -例如使用synchronized或不會引入內存障礙volatile。因此,JVM 可以按如下方式執行它:
public Helper getHelper() {
final Helper toReturn = helper; // "3rd" read, reading null
if (helper == null) { // First read of helper
synchronized (this) {
if (helper == null) { // Second read of helper
helper = new Helper(42);
}
}
}
return toReturn; // Returning null
}
然后您的調用將返回一個空值。然而,會創建一個單例值。但是,后續調用可能仍會獲得空值。
正如所建議的,使用 volatile 會引入新的內存屏障。另一種常見的解決方案是捕獲讀取的值并返回它。
public Helper getHelper() {
Helper singleton = helper;
if (singleton == null) {
synchronized (this) {
singleton = helper;
if (singleton == null) {
singleton = new Helper(42);
helper = singleton;
}
}
}
return singleton;
}
由于您依賴于局部變量,因此無需重新排序。一切都發生在同一個線程中。

TA貢獻1775條經驗 獲得超8個贊
不,這些讀取之間沒有任何傳遞關系。synchornized只保證在同一個鎖的同步塊內所做的更改的可見性。在這種情況下,所有讀取都不會使用同一個鎖上的同步塊,因此這是有缺陷的并且不保證可見性。
因為一旦字段被初始化就沒有鎖定,所以聲明該字段是至關重要的volatile。這將確??梢娦?。
private volatile Helper helper = null;

TA貢獻1852條經驗 獲得超1個贊
這一切都在這里解釋https://shipilev.net/blog/2014/safe-public-construction/#_singletons_and_singleton_factories,問題很簡單。
... 請注意,我們在此代碼中對實例進行了多次讀取,并且至少“讀取 1”和“讀取 3”是沒有任何同步的讀取...規范方面,如發生在一致性規則中所述,讀取操作可以通過競態觀察無序寫入。這是為每個讀取操作決定的,無論其他哪些操作已經讀取了相同的位置。在我們的例子中,這意味著即使“read 1”可以讀取非空實例,代碼然后繼續返回它,然后它進行另一個活潑的讀取,它可以讀取一個空實例,它將被返回!
添加回答
舉報