Versions Compared

Key

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

...

Lazy initialization uses either a class or an instance method, depending on whether the member object is static. The method checks whether the instance has already been created and, if not, creates it. When the instance already exists, the method simply returns the instance:

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;
  }
  // ...
}

Lazy initialization must be synchronized in multithreaded applications to prevent multiple threads from creating extraneous instances of the member object:

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;
  }
  // ...
}

...

The double-checked locking pattern uses block synchronization rather than method synchronization and installs an additional null reference check before attempting synchronization. This noncompliant code example uses an incorrect form of the double-checked locking idiom.

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;
  }

  // Other methods and members...
}

...

This compliant solution declares the helper field volatile.

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();
        }
      }
    }
    return helper;
  }
}

...

This compliant solution initializes the helper field in the declaration of the static variable [Manson 2006].

Code Block
bgColor#ccccff

final class Foo {
  private static final Helper helper = new Helper();

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

...

This compliant solution uses the initialize-on-demand, holder class idiom that implicitly incorporates lazy initialization by declaring a static variable within a static Holder inner class.

Code Block
bgColor#ccccff

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

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

...

This compliant solution (originally suggested by Alexander Terekhov [Pugh 2004]) uses a ThreadLocal object to track whether each individual thread has participated in the synchronization that creates the needed happens-before relationships. Each thread stores a non-null value into its thread-local perThreadInstance only inside the synchronized createHelper() method; consequently, any thread that sees a null value must establish the necessary happens-before relationships by invoking createHelper().

Code Block
bgColor#ccccff

final class Foo {
  private final ThreadLocal<Foo> perThreadInstance = 
      new ThreadLocal<Foo>();
  private Helper helper = null;

  public Helper getHelper() {
    if (perThreadInstance.get() == null) {
      createHelper();
    }
    return helper;
  }

  private synchronized void createHelper() {
    if (helper == null) {
      helper = new Helper();
    }
    // Any non-null value can be used as an argument to set()
    perThreadInstance.set(this);
  }
}

...

In this compliant solution, suppose that the Helper class is immutable. The Java Memory Model (JMM) guarantees that immutable objects are fully constructed before they become visible to any other thread. Additionally, the block synchronization in the getHelper() method suffices to ensure that all methods that can see a non-null value of the helper field have a proper happens-before relationship for the update to the helper reference. This synchronization and the aforementioned JMM guarantee combine to ensure that only fully initialized Helper objects are visible to threads that see non-null values. Consequently, this compliant solution correctly creates both of the needed happens-before relationships.

Code Block
bgColor#ccccff

public final class Helper {
  private final int n;

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

  // Other 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(42);
        }
      }
    }
    return helper;
  }
}

...

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

LCK10-J

low

probable

medium

P4

L3

Automated Detection

Tool
Version
Checker
Description
Coverity7.5

DOUBLE_CHECK_LOCK

FB.DC_DOUBLECHECK

Implemented

Related Guidelines

MITRE CWE

CWE-609. Double-checked locking

...

[API 2006]

 

[Bloch 2001]

Item 48. Synchronize access to shared mutable data

[Bloch 2008]

Item 71. Use lazy initialization judiciously

[JLS 2005]

§12.4, Initialization of Classes and Interfaces

[Pugh 2004]

 

 

      08. Locking (LCK)