Object creation (state initialisation) and thread safety

为君一笑 提交于 2019-12-31 04:08:30

问题


I am look into the book "Java Concurrency in Practice" and found really hard to believe below quoted statement (But unfortunately it make sense).

http://www.informit.com/store/java-concurrency-in-practice-9780321349606

Just wanted to get clear about this 100%

public class Holder {
    private int n;
    public Holder(int n) { this.n = n; }
    public void assertSanity() {
      if (n != n)
       throw new AssertionError("This statement is false.");
      }
}

While it may seem that field values set in a constructor are the first values written to those fields and therefore that there are no "older" values to see as stale values, the Object constructor first writes the default values to all fields before subclass constructors run. It is therefore Possible to see the default value for a field as a stale value

Regarding bolded statement in above,

I am aware that the behaviour BUT now it is clear that this calling hierarchy of constructors is NOT guarantee to be ATOMIC (calling super constructors in single synchronised block that is guarded by a lock), but what would be the solution? imagine a class hierarchy that has more than one level (even it is not recommended, lets assume as it is possible). The above code snippest is a kind of a prototype that we see everyday in most of the projects.


回答1:


You misread the book. It explicitely says:

The problem here is not the Holder class itself, but that the Holder is not properly published.

So the above construct if fine. What's not fine is to improperly publish such an object to other threads. The book explains that in details.




回答2:


When creating a new object things happen sequentially. I don't know the precise order, but it's something like: allocate the space and initialize it to zeroes, then set the fields that get constant values, then set the fields that get calculated values, then run the constructor code. And, of course, it's got to initialize the subclasses in there somewhere.

So if you try to work with an object that is still being constructed, you can see odd, invalid values in the fields. This doesn't usually happen, but ways to do it:

  • Reference a field that doesn't yet have a value during an assignment to another field.

  • Reference a value in the constructor that doesn't get assigned till later in the constructor.

  • Reference a field in an object in a field in an object that was just read from an ObjectInputStream. (OIS often takes a long time to put values in objects it's read.)

  • Before Java 5, something like:

    public volatile MyClass  myObject;
    ...
    myObject = new MyClass( 10 );
    

    could make trouble because another thread could grab the reference to myObject before the MyClass constructor was finished and it would see bad values (zero instead of 10, in this case) inside the object. With Java 5, the JVM is not allowed to make myObject non-null until the constructor is finished.

  • And today you can still set myObject to this within the constructor and accomplish the same thing.

If you're clever, you can also get hold of Class fields before they've been initialized.

In your code example, (n != n) would be true if something changed the value between the two reads of n. I guess the point is n starts as zero, get's set to something else by the constructor, and assertSanity is called during the construction. In this case, n is not volatile so I don't think the assert will ever be triggered. Make it volatile and it will happen once every million times or so if you time everything precisely right. In real life this kind of problem happens just often enough to wreak havoc but rarely enough that you can't reproduce it.




回答3:


I guess theoretically it is possible. It is similar to double checked locking problem.

public class Test {
    static Holder holder;

    static void test() {
        if (holder == null) {
            holder = new Holder(1);
        }
        holder.assertSanity();
    }
...

If test() is called by 2 threads, thread-2 might see the holder in a state when initialization is still in progress so n != n may happen to be true. Here is bytecode for n != n:

ALOAD 0
GETFIELD x/Holder.n : I
ALOAD 0
GETFIELD x/Holder.n : I
IF_ICMPEQ L1

as you can see JVM loads field n to operand stack twice. So it may happen that the first var gets value before init and the seccond after init




回答4:


the comment:

the Object constructor first writes the default values to all fields before subclass constructors run

seems wrong. My prior experience is that the default values for a class are set before its constructor is run. that is a super class will see its init-ed variables set before its constructor runs and does things. This was root of bug a friend looked at where a base class was calling a method during construction that the super class implemented and set a reference that was defined with initialization to null in the super class. the item would be there until entry to the constructor at which time the init set it to null value.

references to the object are not available to another thread (assuming none generated in the constructor) until it completes construction and object reference is returned.



来源:https://stackoverflow.com/questions/19179907/object-creation-state-initialisation-and-thread-safety

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