Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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 as null

...

  • a fully-initialized Helper object with the n field set to 42

...

  • a partially-initialized Helper object with an uninitialized n which contains the default value 0

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 where static final fields can be set. Thus, no special rules in the JMM are needed for static 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.

...