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 thefinal
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'sfinal
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?
Statement 1) does not say what you think it does. If anything, I would rephrase your statement:
to read
where what I mean by mutable is an object that has ANY non-final fields or final references to mutable objects. (have to admit I'm not 100% that you need to worry about final references to mutable objects, but I think I'm right...)
To put it another way, you should distinguish between:
The second one is the problem spot.
So, you can share references to immutable objects (all fields are
final
), but you need to use caution with objects that have non-final
fields that HAVE to be initialized before the object can be used by anyone.In other words, for the edited JLS example you posted where both fields are
final
,int j = f.y;
is guaranteed to be final. But what that means is that you do NOT need to avoid writing the reference to object f, because it'll always be in a correctly initialized state before anyone could see it. You do not need to worry about it, the JVM does.In the full example
As you can see,
f
is not set until after the constructor returns. This meansf.x
is safe because it isfinal
AND the constructor has returned.In the following example, neither value is guarenteed to be set.
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.
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.
Construct exit plays an important role here; the JLS says "A freeze action on final field f of o takes place when c exits". Publishing the reference before/after constructor exit are very different.
Informally
[2] cannot be reordered beyond constructor exit. so [2] cannot be reordered after [5].
but [2] can be reordered after [3].
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.You shouldn't start the thread in a construtor either:
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:
This is the critical difference.