4

I came across the following code in an article somewhere on the Internet:

public class MyInt {

    private int x;

    public MyInt(int y) {
        this.x = y;
    }

    public int getValue() {
        return this.x;
    }
}

The article states that

Constructors are not treated special by the compiler (JIT, CPU etc) so it is allowed to reorder instructions from the constructor and instructions that come after the constructor.

Also, this JSR-133 article about the Java Memory Model states that

A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object’s final fields.

The abovementioned MyInt instance seems immutable (except that the class is not marked final) and thread-safe, but the articles state it is not. They state that it's not guaranteed that x always has the correct value upon read.

But I thought that

only the thread that creates an object should have access to it while it is being constructed

and the Java Tutorials seem so support that.

My question is: does it mean that, with the current JMM, a thread can have access to a partially constructed object due to instruction reordering? And if yes, how? And does that mean that the statement from the Java Tutorials is simply not true?

MC Emperor
  • 22,334
  • 15
  • 80
  • 130

2 Answers2

8

That article is saying that if you have code like

foo = new MyInt(7);

in a class that has a field

MyInt foo;

then the instructions that amount to

(reference to new object).x = 7;
foo = (reference to new object);

could be swapped over as some kind of optimisation. This will never change the behaviour of the thread that's running this code, but it's possible that some other thread will be able to read foo after the line

foo = (reference to new object);

but before the line

(reference to new object).x = 7;

in which case it would see foo.x as 0, not 7. That is to say, that other thread could run

int bar = someObject.getFoo().getValue();

and end up with bar equal to 0.

I've never seen anything like this happen in the wild, but the author seems to know what he's talking about.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • I'm pretty sure the answer lies in the detail of the compiler specification/implementation. While it may be "allowed to reorder instructions from the constructor and instructions that come after the constructor" in general, I'm pretty sure the assignment of that object does not count as a valid instruction to swap. – daniu Aug 24 '17 at 09:52
2

Instruction reordering alone can not lead to another thread seeing a partially constructed object. By definition, the JVM is only allowed to reorder things if they don't affect a correctly synchronized program's behaviour.

It's unsafe publishing of the object reference that enables bad things to happen. Here's a particularly poor attempt at a singleton for example:

public class BadSingleton {
   public static BadSingleton theInstance;

   private int foo;

   public BadSingleton() {
      this.foo = 42;
      if (theInstance == null) {
         theInstance = this;
      }
   }
}

Here you accidentally publish the reference to the object being constructed in a static field. This would not necessarily be a problem until the JVM decides to reorder things and places this.foo = 42 after the assignment to theInstance. So the two things together conspire to break your invariants and allow another thread to see a BadSingleton.theInstance with its foo field uninitialised.

Another frequent source of accidental publication is calling overrideable methods from the constructor. This does not always lead to accidental publication, but the potential is there, hence it should be avoided.

only the thread that creates an object should have access to it while it is being constructed

And does that mean that the statement from the Java Tutorials is simply not true?

Yes and no. It depends on how we interpret the word should. There is no guarantee that in every possible case another thread won't see a partially constructed object. But it's true in the sense that you should write code that doesn't allow it to happen.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • "Instruction reordering alone can not lead to another thread seeing a partially constructed object." Of course it can, as shown in Dawood's answer. The code example you gave has nothing to do with it, it's an example of improper construction. – Oleg Sep 24 '17 at 08:56
  • @Oleg Wrong, his example only works too if the reference to the object is unsafely published. Without unsafe publishing there is no way for another thread to see a partially constructed object. And vice versa, with unsafe publishing you don't need instruction reordering to see a partially constructed object. Therefore instruction reordering **on its own** cannot lead to seeing a partially constructed object. – biziclop Sep 24 '17 at 10:39
  • Sure, but Instruction reordering **can** alone cause a problem when it's unsafely published. It's like saying that a bullet can't kill you if you don't get in it's way. It's clear that an object needs to be unsafely published for a problem to exist, the problem described here is caused by instruction reordering. – Oleg Sep 24 '17 at 10:57
  • @Oleg `Sure, but Instruction reordering can alone cause a problem when it's unsafely published.` So it can't, can it? :) The question was about how it was possible for another thread to see a reference to an object that isn't yet fully constructed. `My question is: does it mean that, with the current JMM, a thread can have access to a partially constructed object due to instruction reordering? And if yes, how? ` And the answer is: only through unsafe publishing, like the example I've just given. – biziclop Sep 24 '17 at 11:09
  • Like I said, it can't in the same way that a bullet can't kill you. The question is specifically about instruction reordering "...partially constructed object **due to instruction reordering?** **And if yes, how?**" Like you said "Instruction reordering around constructors only creates a new way of messing up" the question is about what this "way" is. – Oleg Sep 24 '17 at 12:52
  • @Oleg If your code is otherwise correct, instruction reordering can't cause any such thing, that's the whole point. That's why it is allowed at all. – biziclop Sep 24 '17 at 12:54
  • But you can say that about anything, if some code can have bug y because of x but you write code that has no problem with x happening then you can't have bug y because of x. Isn't that like obvious? Why is that an important point? In this case the question is about why x can cause y and not how to write correct code for z. – Oleg Sep 24 '17 at 13:07
  • @Oleg Again, let's go back to the question. "Can instruction reordering cause another thread to see a half-constructed object?" No, on its own it can't. In other words, it isn't something that you need to worry about. (Compare that with let's say deserialisation, which **can** break otherwise correct code, and you have to design your code in a way that protects from it.) And that's my final word on it really because I think we're thinking broadly the same thing, and we're really arguing on nomenclature. – biziclop Sep 24 '17 at 13:11
  • 1
    Alright, anyway I removed my downvote. My final word on the same question is yes it can create problems on it's own when the object is unsafely published and I agree with the rest of you comment, we understand each other and mostly argue on semantics. – Oleg Sep 24 '17 at 13:22
  • 1
    @Oleg Thank you, I re-read my answer and I think I understand your concern better now. I've changed the example to demonstrate how exactly the two things can interact and mess things up. – biziclop Sep 24 '17 at 18:23