的我的問題是給帖子: https://shipilev.net/blog/2014/safe-public-construction/Java內存模型和重排序操作
public class UnsafeDCLFactory {
private Singleton instance;
public Singleton get() {
if (instance == null) { // read 1, check 1
synchronized (this) {
if (instance == null) { // read 2, check 2
instance = new Singleton(); // store
}
}
}
return instance; // read 3
}
}
而且,它寫的是:在這個代碼實例
請注意,我們做的幾個讀,並且至少「讀取1」和「讀取3」是沒有任何同步的讀取 - 也就是說,這些讀取是活潑的。 Java內存模型 的其中一個意圖是允許重新排序以進行普通讀取,否則性能成本會過高。 在規則方面,正如前面提到的一致性規則一樣, 讀取操作可以觀察通過競爭的無序寫入。這是 決定的每個讀取操作,無論其他操作 已讀取相同的位置。在我們的例子中,這意味着即使 雖然「讀取1」可以讀取非null實例,然後代碼在 上移動以返回它,然後它執行另一個讀取讀取,並且它可以讀取 null實例,這將是回!
我無法理解它。我同意編譯器顯然可以重新排序內存操作。但是,這樣做,編譯器必須從單線程的點視圖保留原始程序的行爲。
在上例中,read 1
讀取非空。 read 3
閱讀null。這意味着編譯器對read 3
進行了重新排序,並且讀取instance
的優先級爲read 1
(我們可以跳過CPU重新排序,因爲該帖子會引發Java內存模型)。
但是,在我的眼睛read 3
不能超越read 1
因爲店操作 - 我的意思是instance = new Singleton();
畢竟,有數據依賴關係,這意味着編譯器不能重新排序指令read 3
與store
,因爲它改變的意義該程序(甚至是單線程)。編譯器也不能更改read 1
的順序,因爲它必須在store
之前。否則,單線程程序的語義是不同的。
因此,順序必須是:read 1 -> store -> read 3
你怎麼看呢?
P.S.發佈什麼意味着什麼?特別是,發佈一些不安全的東西意味着什麼?
這是對@Aleksey Shipilev答案的重新回答。
讓我再說一遍 - 構建示例失敗並不能否定規則。
是的,很明顯。
而Java Memory Model允許在第二次讀取時返回null。
我同意這一點。我不認爲它不允許。 (可能是因爲數據競賽 - 是的,它們是邪惡的)。我聲稱read 3
不能超過read 1
。我知道你是對的,但我想明白這一點。我仍然聲稱Java編譯器無法生成read 3
接管read 1
的此類字節碼。我看到read3
由於數據競爭可以讀取null
,但我無法想象read 1
讀取非null和read 3
讀取爲空而read 3
由於數據依賴性無法超越read 1
。
(這裏不考慮對硬件(CPU)級的存儲排序)
「編譯器必須從一個線程的角度出發保持原始程序的behaviuor」 - 是什麼讓你說呢? – user2357112
另外,如果讀取1讀取非空值,則此線程**不會執行存儲**。沒有要求保持相對於沒有發生的操作的順序。其他一些線程可能會執行一個存儲,但是如果沒有同步,相對於其他線程存儲的訂購要求相當寬鬆。 – user2357112
「這個線程不執行存儲」,是的,但是當編譯器編譯代碼時,它不知道'instance'是否爲null。 – Gilgamesz