I was studying this to understand the behavior of final fields in the new JMM (5 onwards). This concept is clear: guaranteed visibility of initialized final fields to all threads after the object is properly constructed.
But then at the end of the section, I read this, which simply confuses me:
Now, having said all of this, if, after a thread constructs an immutable object (that is, an object that only contains final fields), you want to ensure that it is seen correctly by all of the other thread, you still typically need to use synchronization. There is no other way to ensure, for example, that the reference to the immutable object will be seen by the second thread.
Does this means that though individual final fields (that compose an immutable object) do not have synchronization(say, visibility here) issues. But the immutable object itself when first created in a thread may not be visible (as properly created) in other threads?
If so, though we can share initialized immutable objects across threads without any thread-un-safe worries, but at the time of creation, they need 'special care' for thread safety just like for other mutables?
Making all the fields
final
will ensure they are published to other threads properly. That comment is probably referring to the following scenario:In this case you still need proper synchronization around any access to
myField
, or other threads might never see the newly created object.I believe author referred to the situation when immutable object is referenced by non-
final
field. If reference itself isfinal
, additional synchronization is not required.Additional consideration is that above applies only to the object fields which are initialized inside object's constructor.
I would be slightly leery of a text that turns typically into no other way in the space of a sentence. In fact, which is true depends on what exactly we mean by "use synchronization".
The relevant parts of the Java Language Specification are:
and
Happens-before can be established in a number of ways:
where
By making the fields final, you ensure their assignment happens-before the completion of the constructor. What you still need to ensure is that the completion of the constructor happens-before the object is accessed. If that access occurs in a different thread, you need to establish synchronizes-with, using any of the 6 ways shown above. Typically used are:
Declare the field that other threads use to access the object volatile. For instance:
Do both the initial assignment and the reading of the field in a synchronized block.
The semantics of final fields, as defined in section 17.5 of the JLS, guarantee that:
In other words, it says that if a thread sees a completely initialized object, then it is guaranteed to see it's final fields correctly initialized.
However, there's no guarantee about the object being visible to a given thread. It is a different problem.
If you do not use some kind of synchronization to publish a reference of your object, then the other thread might never be able to see a reference to it.
Consider the following code:
Note that the
a1
field is volatile. This ensures that, eventually, a write to this field will be made visible to all threads reading it some time later. The fielda2
is not volatile (so, a write to this field by one thread might never get noticed by other threads).In this code, we can be sure that thread 1 will finish executing (that is, it will see that
a1 != null
. However, it might happen that thread 2 will halt, as it will never see the write to the fielda2
, since it is not volatile.