[英]Is reordering of instance initialization and assignment to a shared variable possible?
我正在閱讀一篇文章 ,其中涉及雙重檢查鎖定,但我對作為示例提供的代碼中更基本的失敗感到驚訝。 在那里聲明,實例的初始化(即,在構造函數返回之前發生的實例變量的寫入)可能會在對實例的引用寫入共享變量(在靜態字段中) 之后重新排序。以下示例)。
使用以下Foo
類的定義是否正確,一個線程執行Foo.initFoo();
和另一個執行System.out.println(Foo.foo.a);
線程System.out.println(Foo.foo.a);
,第二個線程可能會打印0
(而不是1
或拋出NullPointerException
)?
class Foo {
public int a = 1;
public static Foo foo;
public static void initFoo() {
foo = new Foo();
}
public static void thread1() {
initFoo(); // Executed on one thread.
}
public static void thread2() {
System.out.println(foo.a); // Executed on a different thread
}
}
根據我對Java內存模型(以及其他語言中的內存模型)的了解,實際上我並不感到驚訝,這是可能的,但直覺投票非常強烈,因為它是不可能的(可能因為涉及對象初始化而對象初始化似乎如此在Java中神聖)。
是否可以在第一個線程中沒有同步的情況下“修復”此代碼(即它永遠不會打印0
)?
調用foo = new Foo();
涉及幾個可能重新排序的操作,除非你引入適當的同步來防止它:
a = 0
) a = 1
) 如果沒有正確的同步,可能會重新排序步驟3和4(請注意,步驟2必須在步驟4之前發生),盡管x86架構上的熱點不太可能發生。
為了防止它你有幾個解決方案,例如:
a
決賽 foo
(使用同步的init
AND getter)。 在不進入JLS#17的復雜性的情況下,您可以閱讀關於類初始化的JLS#12.4.1 (強調我的):
初始化代碼不受限制的事實允許構造示例,其中在評估其初始化表達式之前,當它仍然具有其初始默認值時可以觀察到類變量的值 ,但是這樣的示例在實踐中是罕見的。 ( 這些示例也可以構造為例如變量初始化 。)這些初始化器中可以使用Java編程語言的全部功能。 程序員必須小心謹慎。 這種能力給代碼生成器帶來了額外的負擔,但是在任何情況下都會產生這種負擔,因為Java編程語言是並發的。
即使在x86下,JIT編譯器的實例初始化重新排序也是可能的。 但是,編寫可以觸發此類重新排序的代碼有點棘手。 關於如何重現這種重新排序,請參閱我的問題:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.