...
This noncompliant code example constructs a Helper
object in the initialize()
method of the Foo
class. The helper
Helper
object's fields are initialized by its constructor.
...
If a thread accesses helper
using the getHelper()
method before the initialize()
method has been called, the thread will observe an uninitialized helper
field. Later, if one thread calls initialize()
, and another calls getHelper()
, the second thread might observe one of the following: *
- 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 assign it to the helper
field before initializing it. In other words, the compiler can reorder the write to the helper
instance field with the write that initializes the Helper
object (that is, this.n = n
) such that the former occurs first. This exposes a race window during which other threads may observe a partially-initialized Helper
object instance.
...
Wiki Markup |
---|
However, this solution requires the assignment of a new {{Helper}} instance to {{helpgerhelper}} from Foo's constructor. According to the _Java Language Specification_, Section 17.5.2, "Reading Final Fields During Construction" \[[JLS 2005|AA. Java References#JLS 05]\] |
...
The helper
field is declared final to guarantee that the vector is created before any accesses take place. It can be initialized safely by invoking the synchronized initialize()
method, which ensures that only one Helper
object os is ever added to the vector. If getHelper()
is invoked before initialize()
, it calls initialize()
to avoid the possibility of a null-pointer de-reference by the client. The getHelper()
method does not require synchronization to simply return Helper
, andâ”because the synchronized initialize()
method also checks to make sure helper
is empty before adding a new Helper
objectâ”there is no possibility of exploiting a race condition to add a second object to the vector.
...
In this compliant solution, the helper
field is statically initialized, ensuring that the object referenced by the field is fully initilalized initialized before its reference is visible.
...
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 wherestatic final
fields can be set. Thus, no special rules in the JMM are needed forstatic final
fields.
Compliant Solution (Immutable
...
Object - Final
...
Fields, Volatile Reference)
Wiki Markup |
---|
The JMM guarantees that any final fields of an object are fully initialized before a published object becomes visible \[[Goetz 2006|AA. Java References#Goetz 06]\]. By declaring {{n}} final, the {{Helper}} class is made [immutable|BB. Definitions#immutable]. Furthermore, if the {{helper}} field is declared volatile in compliance with guideline [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()}} after {{Helper}} has been fully initialized. |
...
If the helper
field in the Foo
class is not declared volatile, the n
field should be declared volatile so that a happens-before relationship is established between the initialization of n
and the write of Helper
to the helper
field. This is in compliance with guideline VNA06-J. Do not assume that declaring an object reference volatile guarantees visibility of its members. This is required only when the caller (class Foo
) cannot be trusted to declare helper
volatile.
Because the the Helper
class is declared public, it uses a private lock to handle synchronization in conformance with guideline LCK00-J. Use private final lock objects to synchronize classes that may interact with untrusted code.
...