During initialization of a shared object, the object must be accessible only to the thread constructing it. However, the object can be published safely (that is, made visible to other threads) once its initialization is complete. The Java Memory Model (JMM) allows multiple threads to observe the object after its initialization has begun but before it has concluded. Consequently, programs must prevent publication of partially initialized objects.
...
- The
helper
reference asnull
- A fully initialized
Helper
object with then
field set to 42 - A partially initialized
Helper
object with an uninitializedn
, which contains the default value0
In particular, the JMM permits compilers to allocate memory for the new Helper
object and to assign a reference to that memory to the helper
field before initializing the new Helper
object. In other words, the compiler can reorder the write to the helper
instance field and the write that initializes the Helper
object (that is, this.n = n
) so that the former occurs first. This can expose a race window during which other threads can observe a partially initialized Helper
object instance.
...
Synchronizing both methods guarantees that they cannot execute concurrently. If one thread were to call initialize()
just before another thread called getHelper()
, the synchronized initialize()
method would always finish first. The synchronized
keywords establish a happens-before relationship between the two threads. Consequently, the thread calling getHelper()
would see either the fully initialized Helper
object or an absent Helper
object (that is, helper
would contain a null
reference). This approach guarantees proper publication both for immutable and mutable members.
...
However, this solution requires the assignment of a new Helper
instance to helper
from Foo's constructor. According to the Java Language Specification, §17.5.2, "Reading Final Fields During Construction" [JLS 2005]
A read of a final field of an object within the thread that constructs that object is ordered with respect to the initialization of that field within the constructor by the usual happens-before rules. If the read occurs after the field is set in the constructor, it sees the value the final field is assigned; otherwise, it sees the default value.
...
According to JSR-133, Section 9.2.3, "Static Final Fields" [JSR-133 2004]
The rules for class initialization ensure that any thread that reads a
static
field will be synchronized with the static initialization of that class, which is the only place where static final fields can be set. Thus, no special rules in the JMM are needed for static final fields.
...
The JMM guarantees that any final fields of an object are fully initialized before a published object becomes visible [Goetz 2006a]. By declaring n
final, the Helper
class is made immutable. Furthermore, if the helper
field is declared volatile in compliance with rule VNA01-J. Ensure visibility of shared references to immutable objects, Helper
's reference is guaranteed to be made visible to any thread that calls getHelper()
only after Helper
has been fully initialized.
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
TSM03-J | medium | probable | medium | P8 | L2 |
Bibliography
[API 2006] |
|
Item 48. Synchronize access to shared mutable data | |
Section 3.5.3, Safe Publication Idioms | |
Pattern #2, One-Time Safe Publication | |
[JPL 2006] | 14.10.2, Final Fields and Security |
|
...
Rule 12: Thread-Safety Miscellaneous (TSM) Rule 13: Input Output (FIO)