Reputation: 1015
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.
Upvotes: 1
Views: 188
Reputation: 12630
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.
Upvotes: 0