Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

About reference to object before object's constructor is finished

Every one of you know about this feature of JMM, that sometimes reference to object could receive value before constructor of this object is finished.

In JLS7, p. 17.5 final Field Semantics we can also read:

The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. (1)

And just after that in JLS the example follows, which demonstrate, how non-final field is not guaranteed to be initialized (1Example 17.5-1.1) (2):

class FinalFieldExample { 
    final int x; 
    int y; 

    static FinalFieldExample f;

    public FinalFieldExample() { 
        x = 3; 
        y = 4; 
    } 

    static void writer() { 
        f = new FinalFieldExample(); 
    } 

    static void reader() { 
       if (f != null) { 
           int i = f.x; // guaranteed to see 3 
           int j = f.y; // could see 0 
       } 
    } 
}

Also, in this question-answer Mr. Gray wrote:

If you mark the field as final then the constructor is guaranteed to finish initialization as part of the constructor. Otherwise you will have to synchronize on a lock before using it. (3)


So, the question is:

1) According to statement (1) we should avoid sharing reference to immutable object before its constructor is finished

2) According to JLS's given example (2) and conclusion (3) it seems, that we can safely share reference to immutable object before its constructor is finished, i.e. when all its fields are final.

Isn't there some contradiction?


EDIT-1: What I exactly mean. If we will modify class in example such way, that field y will be also final (2):

class FinalFieldExample { 
    final int x; 
    final int y; 
    ...

hence in reader() method it will be guaranteed, that:

if (f != null) { 
int i = f.x; // guaranteed to see 3
int j = f.y; // guaranteed to see 4, isn't it???

If so, why we should avoid writing reference to object f before it's constructor is finished (according to (1)), when all fields of f are final?

like image 968
Andremoniy Avatar asked Feb 01 '13 19:02

Andremoniy


People also ask

What is the reference to an object?

The concept of object references becomes clear when assigning the same object to more than one property. Rather than holding a copy of the object, each assigned property holds object references that link to the same object, so that when the object changes all properties referring to the object reflect the change.

What is the purpose of an object reference?

You use an object reference variable to create, manage, and delete objects. It has the data type of a class and, like other variables, is a named area in storage. However, unlike other variables, the value stored in the area is not the object itself but a 4-byte pointer to the object data, called an object reference.

What is object and reference of object?

In short, object is an instance of a class and reference (variable) points out to the object created in the heap area.

What happens when constructor is final?

No, a constructor can't be made final. A final method cannot be overridden by any subclasses. As mentioned previously, the final modifier prevents a method from being modified in a subclass. The main intention of making a method final would be that the content of the method should not be changed by any outsider.


2 Answers

Isn't there some contradiction [in the JLS around constructors and object publishing]?

I believe these are slightly different issues that are not contradictory.

The JLS reference is taking about storing an object reference in a place where other threads can see it before the constructor is finished. For example, in a constructor, you should not put an object into a static field that is used by other threads nor should you fork a thread.

  public class FinalFieldExample {
      public FinalFieldExample() {
         ...
         // very bad idea because the constructor may not have finished
         FinalFieldExample.f = this;
         ...
      }
  }

You shouldn't start the thread in a construtor either:

  // obviously we should implement Runnable here
  public class MyThread extends Thread {
      public MyThread() {
         ...
         // very bad idea because the constructor may not have finished
         this.start();
      }
  }

Even if all of your fields are final in a class, sharing the reference to the object to another thread before the constructor finishes cannot guarantee that the fields have been set by the time the other threads start using the object.

My answer was talking about using an object without synchronization after the constructor had finished. It's a slightly different question although similar with regards to constructors, lack of synchronization, and reordering of operations by the compiler.

In JLS 17.5-1 they don't assign a static field inside of the constructor. They assign the static field in another static method:

static void writer() {
    f = new FinalFieldExample();
}

This is the critical difference.

like image 199
Gray Avatar answered Oct 02 '22 02:10

Gray


In the full example

class FinalFieldExample { 
    final int x;
    int y; 
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3; 
        y = 4; 
    } 

    static void writer() {
        f = new FinalFieldExample();
    } 

    static void reader() {
        if (f != null) {
            int i = f.x;  // guaranteed to see 3  
            int j = f.y;  // could see 0
        } 
    } 
}

As you can see, f is not set until after the constructor returns. This means f.x is safe because it is final AND the constructor has returned.

In the following example, neither value is guarenteed to be set.

class FinalFieldExample { 
    final int x;
    int y; 
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3; 
        y = 4; 
        f = this; // assign before finished.
    } 

    static void writer() {
        new FinalFieldExample();
    } 

    static void reader() {
        if (f != null) {
            int i = f.x;  // not guaranteed to see 3  
            int j = f.y;  // could see 0
        } 
    } 
}

According to statement (1) we should avoid sharing reference to immutable object before its constructor is finished

You should not allow a reference to an object escape before it is constructed for a number of reason (immutable or other wise) e.g. the object might throw an Exception after you have store the object.

According to JLS's given example (2) and conclusion (3) it seems, that we can safely share reference to immutable object, i.e. when all its fields are final.

You can safely share a reference to an immutable object between threads after the object has been constructed.

Note: you can see the value of an immutable field before it is set in a method called by a constructor.

like image 38
Peter Lawrey Avatar answered Oct 02 '22 01:10

Peter Lawrey