I was reading an essay, which actually talks about double-checked locking, but I'm surprised about an even more basic failure in the code presented as examples. It is stated there that it is possible that the initialization of an instances (i.e. writes to the instance variables that happen before the constructor returns) may be reordered to after a reference to the instance is written to a shared variable (a static field in the following example).
Is it true that with the following definition of class Foo
, with one thread executing Foo.initFoo();
and a different thread executing System.out.println(Foo.foo.a);
, the second thread may print 0
(instead of 1
or throwing a 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
}
}
From what I know about the Java memory model (and memory models in other languages) it actually doesn't surprise me that this is possible but intuition is voting very strongly for it being impossible (maybe because object initialization is involved and object initialization seems so sacred in Java).
Is it possible to "fix" this code (i.e. that it will never print 0
) without synchronization in the first thread?
A call to foo = new Foo();
involves several operations which might be reordered unless you introduce proper synchronization to prevent it:
a = 0
)a = 1
)Without proper synchronization, steps 3 and 4 might be reordered (note that step 2 necessarily happens before step 4), although it is unlikely to happen with hotspot on a x86 architecture.
To prevent it you have several solutions, for example:
a
finalfoo
(with a synchronized init
AND getter).Without going into the intricacies of JLS #17, you can read JLS #12.4.1 about class initialization (emphasis mine):
The fact that initialization code is unrestricted allows examples to be constructed where the value of a class variable can be observed when it still has its initial default value, before its initializing expression is evaluated, but such examples are rare in practice. (Such examples can be also constructed for instance variable initialization.) The full power of the Java programming language is available in these initializers; programmers must exercise some care. This power places an extra burden on code generators, but this burden would arise in any case because the Java programming language is concurrent.
Instance initialization reordering by JIT compiler IS possible even under x86. However, it is somewhat tricky to write code that can trigger such reordering. On how to reproduce such reordering, see my question:
Is there any instruction reordering done by the Hotspot JIT compiler that can be reproduced?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With