Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: edits

Wiki Markup
The usual way of initializing an object is to use a constructor. Sometimes it is required to limit the number of instances of a member object to just one (this is similar to a singleton, however, the member object may or may not be {{static}}). In additionInstead of initializing using a constructor, sometimes a technique called lazy initialization is used to defer the construction of the member object until it is actually required. Reasons for incorporating lazy initialization include optimizing and breaking harmful circularities in class and instance initialization \[[Bloch 05|AA. Java References#Bloch 05]\].

For these purposes, instead of a constructor, a A class or an instance method is frequently used for lazy initialization, depending on whether the member object is static or not. The method checks whether the instance has already been created and if not, creates it. If the instance already exists, it simply returns it. This is shown below:

Code Block
bgColor#ccccff
// Correct single threaded version using lazy initialization
final class Foo { 
  private Helper helper = null;
  
  public Helper getHelper() {
    if (helper == null) {
      helper = new Helper();
    }
    return helper;
  }
  // ...
}

...

Code Block
bgColor#ccccff
// Correct multithreaded version using synchronization
final class Foo { 
  private Helper helper = null;
  
  public synchronized Helper getHelper() {
    if (helper == null) {
      helper = new Helper();
    }
    return helper;
  }
  // ...
}

...

Code Block
bgColor#FFCCCC
// "Double-Checked Locking" idiom
final class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) { 
      synchronized(this) {
        if (helper == null) {
          helper = new Helper();
        }
      }    
    }
    return helper;
  }
  // otherOther functionsmethods and members...
}

Wiki Markup
According to the Java Memory Model (discussion reference) \[[Pugh 04|AA. Java References#Pugh 04]\]:

...

This makes the originally proposed double-checked locking pattern insecure. The rule guideline CON26-J. Do not publish partially initialized objects further discusses further the possibility possible occurrence of a non-null reference to a helper object that observes default values for of fields in the helper object.

...

This compliant solution declares the Helper object as volatile and consequently, uses the correct form of the double-check checked locking idiom.

Code Block
bgColor#ccccff
// Works with acquire/release semantics for volatile
// Broken under JDK 1.4 and earlier
final class Foo {
  private volatile Helper helper = null;
  
  public Helper getHelper() { 
    if (helper == null) {
      synchronized(this) {
        if (helper == null) {
          helper = new Helper(); // If the helper is null, create a new instance
        }
      }
    }
    return helper; // If helper is non-null, return its instance
  }
}

...

Wiki Markup
This compliant solution initializes the {{helper}} in the declaration of the {{static}} variable \[[Manson 06|AA. Java References#Manson 06]\]. 

Code Block
bgColor#ccccff
final class Foo {
  private static final Helper helper = new Helper(42);

  public static Helper getHelper() {return helper;}
}

...

This compliant solution explicitly incorporates lazy initialization. It also uses a static variable as suggested in the previous compliant solution. The variable is declared within a static inner class, Holder.

Code Block
bgColor#ccccff
final class Foo {
  static Helper helper;

  // Lazy initialization 
  private static class Holder {
    static Helper helper = new Helper();
  }

  public static Helper getInstance() {
    return Holder.helper;
  }
}

...

Code Block
bgColor#ccccff
// Uses atomic utilities
final class Foo {
  private final AtomicReference<Helper> helperRef =
    new AtomicReference<Helper>();

  public Helper getHelper() {
    Helper helper = helperRef.get();
    if (helper != null) {
      return helper;
    }
    Helper newHelper = new Helper();
    return helperRef.compareAndSet(null, newHelper) ?
           newHelper :
           helperRef.get();
  }
}

Note that while this code ensures that only one Helper}] object is preserved, it may potentially allow multiple {{Helper objects to be created; with all but one being garbage-collected. But However, if constructing multiple Helper objects is infeasible or expensive, then this solution might not may be appropriateinappropriate.

Compliant Solution (immutable)

In this compliant solution the Foo class is unchanged (it is immutable as before), but the Helper class is made immutable. In this caseConsequently, the Helper class is guaranteed to be fully constructed before becoming visible. The object must be truly immutable; it is not sufficient for the program to refrain from modifying the object.

Code Block
bgColor#CCCCFF
public final class Helper {
  private final int n;

  public Helper(int n) {
    this.n = n;
  }

  // otherOther fields &and methods, all fields are final
}

final class Foo {
  private Helper helper = null;
  
  public Helper getHelper() { 
    if (helper == null) {
      synchronized(this) {
        if (helper == null) {
          helper = new Helper(); // If the helper is null, create a new instance
        }
      }
    }
    return helper; // If helper is non-null, return its instance
  }
}

Note that if class Foo was were mutable, the Helper field would need to be declared volatile as shown in CON09-J. Ensure visibility of shared references to immutable objects. Also, the method getHelper() is an instance method and the accessibility of the helper field is private. This allows safe publication of the Helper object, in that, a thread cannot observe a partially initialized Foo object (CON26-J. Do not publish partially initialized objects). The class Helper is also compliant with CON26-J. Do not publish partially initialized objects and consequently, cannot be observed in a partially initialized state.

...