1

I'm reading "Java concurrency in practice", and it says:

To publish an object safely, both the reference to the object and the object's state must be made visible to other threads at the same time. A properly constructed object can be safely published by:

  • Initializing an object reference from a static initializer;
  • Storing a reference to it into a volatile field or AtomicReference;
  • Storing a reference to it into a final field of a properly constructed object; or
  • Storing a reference to it into a field that is properly guarded by a lock.

What I don't understand is "Storing a reference to it into a final field of a properly constructed object", Why is "of a properly constructed object" needed? Without "of a properly constructed object", can other threads see the object that is being published in an inconsistent state?

I have read several related questions:

But I can't find much explanation about Why "of a properly constructed object" is needed.

Below example comes from an answer in question "final vs volatile guaranntee w.rt to safe publication of objects - Stack Overflow"

class SomeClass{
    private final SomeType field;

    SomeClass() {
        new Thread(new Runnable() {
            public void run() {
                SomeType copy = field; //copy could be null
                copy.doSomething(); //could throw NullPointerException
            }
        }).start();
        field = new SomeType();
    }
}

SomeClass is not properly constructed, of course, copy could be null, but in my opinion, the thread can not see the copy in an inconsistent state, either copy is null or "both the reference to the copy and the copy's state must be made visible to the threads at the same time". So, field is published safely even though SomeClass is not properly constructed. Am I right?

Hope someone can give me more explanation, thanks in advance.

Jason Law
  • 965
  • 1
  • 9
  • 21

1 Answers1

0

It depends on what you call "consistent state". If seeing a null pointer where a published object should be, i.e. the object actually looks as if it was not published, counts as "consistent" then you're right in that the example produces "consistency".

Do note however that final fields are supposed to not change their value. If a thread reads from a final it can safely assume that the field's value will not change later. The thread's implementation, or the (JIT) compiler, may 'cache' the field's value in some variable (or register) because final tells it that the value read once stays the same.

Specifically, code like

new Thread(new Runnable() {
    public void run() {
        while ( field == null ) {
          // Wait a little...
        }
        // Field was initialized, go ahead.
    }
}).start();

Will probably only have two possible outcomes: Either the loop is never entered, or it becomes an infinite loop.

That's why it is especially not safe to access a final field before it is initialized; and final fields are only guaranteed to be initialized after the constructor completed.

The issue may become more apparent if you write the fully qualified field name in the thread's code, which is SomeClass.this.field. You are allowed to omit it but the compiler will implicitly generate the proper access for you. Using the fully qualified field name you can see more clearly that the thread accesses SomeClass.this.field before this is fully initialized. So, effectively, you don't actually publish the consistent SomeType object but the still inconsistent SomeClass object which happens to contain that field.

JimmyB
  • 12,101
  • 2
  • 28
  • 44
  • What do you mean by "partially constructed field". *Fields* are not constructed. – JimmyB Apr 12 '19 at 13:28
  • However, the JLS guarantees that accesses to any field are *atomic*. I.e., no Java code will ever see an invalid object reference due to a race condition. The field will either contain `null` *or* a valid object reference, never anything in between. – JimmyB Apr 12 '19 at 13:43
  • Okay, what about this one? `new Thread(new Runnable() { public void run() { while ( field == null ) { // Wait a little... } // Field was initialized, go ahead. } }).start();`. Can the thread see a partially constructed `field` (the reference to the `field` is not null but some attributes of `field` are null) after exiting the while loop? – Jason Law Apr 12 '19 at 13:46
  • No, because in the constructor an object is created by `new SomeType()`, which runs `SomeType`'s constructor and only after the creation of the `SomeType` instance is complete is the reference to the new object is stored into `field`. – JimmyB Apr 12 '19 at 13:49
  • I don't think I actually get your question. The quote from the book is correct and complete; there's no use in trying to come up with some construct to prove it wrong while at the same time disregarding the relation of such construct to the statements from the book. – JimmyB Apr 12 '19 at 13:55
  • Okay, thanks for your reply. by the way, my question is why both final and properly constructed are needed to publish an object safely? Isn't final itself sufficient? – Jason Law Apr 12 '19 at 14:07
  • Ok, I see. I imagine (but would have to dig through the JLS to verify) that it is only guaranteed that field assignments are visible after the constructor has exited. This should match the assertions w.r.t. non-`volatile` fields assigned in normal methods, i.e. `final` fields are treated the same as any other field assignment, just that the only 'method' in which they can be assigned is the constructor. – JimmyB Apr 12 '19 at 14:38