Partial constructed objects in the Java Memory Model

只愿长相守 提交于 2019-12-21 20:34:36

问题


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?


回答1:


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.




回答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.



来源:https://stackoverflow.com/questions/45857765/partial-constructed-objects-in-the-java-memory-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!