14

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?

Feuermurmel
  • 9,490
  • 10
  • 60
  • 90
  • 1
    Your code seems missing System.out.println() like you described. Can you post the complete code? – IndoKnight Apr 15 '13 at 08:57
  • 1
    It is also missing the call to `Foo.initFoo()`. I've added the statements as separate static function which would be called on different threads without synchronization between the threads. – Feuermurmel Apr 15 '13 at 09:31

2 Answers2

8

A call to foo = new Foo(); involves several operations which might be reordered unless you introduce proper synchronization to prevent it:

  1. allocate memory for the new object
  2. write the default values of fields (a = 0)
  3. write the initial values of fields (a = 1)
  4. publish the reference to the newly created object

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:

  • make a final
  • synchronize access to foo (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.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • Your answer is a relief! I was so used to the fact that publishing an _immutable_ object by setting a shared variable is thread-safe without synchronization that I never thought about the fact that it's only thread safe _because_ of the object's immutability! – Feuermurmel Apr 15 '13 at 11:14
  • 1
    @Feuermurmel There are two things you should know 1. Declaring the field `volatile` is sufficient for necessary synchronization to prevent re-ordering that assylias mentioned. 2. A fully immutable object (assuming all fields are final) does not run the risk of partially initialized objects that we are seeing to be a problem here. – John Vint Apr 15 '13 at 11:16
  • @JohnVint Does that mean that declaring the static field `Foo.foo` _transient_ instead of the field `Foo.a` _final_ would also solve the problem (assuming there is no other place where Foo.a is written to)? – Feuermurmel Apr 15 '13 at 11:37
  • @Feuermurmel Not transient, volatile. And yes, with `public static volatile Foo foo;`, you also have a safe publication and `a` is guaranteed to be seen as 1. – assylias Apr 15 '13 at 11:51
  • @GaborSch Oh, wow, I failed hard! I meant `volatile` of course! – Feuermurmel Apr 15 '13 at 11:57
  • @Feuermurmel Yes volatile. So if `a` is final you will have safe publication or if `foo` is volatile. As far as final, the JLS specifies that all final fields are guaranteed to be visible after the construction of an object. – John Vint Apr 15 '13 at 12:07
  • This claim is incorrect: although it is unlikely to happen with hotspot on a x86 architecture. I had reproduced it under x86 using Hotspot. see: http://stackoverflow.com/questions/35883354/is-there-any-instruction-reordering-done-by-the-hotspot-jit-compiler-that-can-be – user2351818 Mar 11 '16 at 07:56
  • @user2351818 I said it was unlikely to happen because the x86 architecture has a fairly strong memory model. I didn't say that it can't happen. Interesting link though. – assylias Mar 11 '16 at 15:59
  • @assylias To make it clear: what we discussed are two kinds of reordering: one is memory reordering done by CPU Store Buffer/Cache, the other is compiler reordering done by JIT compiler. – user2351818 Mar 12 '16 at 11:37
3

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?

Community
  • 1
  • 1
user2351818
  • 557
  • 2
  • 15