Double-checked locking on non-volatile fields is unsafe


The problem

Using double-checked locking on mutable objects in non-volatile fields is not thread-safe.

If the field is not volatile, the compiler may re-order the code in the accessor. For more information, see:

The canonical example of correct double-checked locking for lazy initialization is:

class Foo {
  /** This foo's bar.  Lazily initialized via double-checked locking. */
  private volatile Bar bar;

  public Bar getBar() {
    Bar value = bar;
    if (value == null) {
      synchronized (this) {
        value = bar;
        if (value == null) {
          bar = value = computeBar();
    return value;

  private Bar computeBar() { ... }


Double-checked locking should only be used in performance critical classes. For code that is less performance sensitive, there are simpler, more readable approaches. Effective Java recommends two alternatives:

Synchronized Accessors

For lazily initializing instance fields, consider a synchronized accessor. In modern JVMs with efficient uncontended synchronization the performance difference is often negligible.

// Lazy initialization of instance field - synchronized accessor
private Object field;
synchronized Object get() {
  if (field == null) {
    field = computeValue();
  return field;

Holder Classes

If the field being initialized is static, consider using the lazy initialization holder class idiom:

// Lazy initialization holder class idiom for static fields
private static class Holder {
  static final Object field = computeValue();
static Object get() {
  return Holder.field;

Double-checked locking and immutability

If the object being initialized with double-checked locking is immutable, then it is safe for the field to be non-volatile. However, the use of volatile is still encouraged because it is almost free on x86 and makes the code more obviously correct.

Note that immutable has a very specific meaning in this context:

[An immutable object] is transitively reachable from a final field, has not changed since the final field was set, and a reference to the object containing the final field did not escape the constructor.

Double-checked locking on non-volatile fields is in general unsafe because the compiler and JVM can re-order code from the object’s constructor to occur after the object is written to the field.

The final modifier prevents that re-ordering from occurring, and guarantees that all of the object’s final fields have been written to before a reference to that object is published.


Suppress false positives by adding the suppression annotation @SuppressWarnings("DoubleCheckedLocking") to the enclosing element.