Versions Compared

Key

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

...

Code Block
bgColor#ccccff
class Foo {
  // If perThreadInstance.get() returns a non-null value, this thread
  // has done synchronization needed to see initialization of helper

  private final ThreadLocal perThreadInstance = new ThreadLocal();
  private Helper helper = null;

  public Helper getHelper() {
    if (perThreadInstance.get() == null) {
      createHelper();
    }
    return helper;
  }
        
  private final void createHelper() {
    synchronized(this) {
      if (helper == null) {
        helper = new Helper();
      }
      // Any non-null value would do as the argument here
      perThreadInstance.set(perThreadInstance);
    }
  }
}

Compliant Solution (java.util.concurrent utilities)

This compliant solution uses an AtomicReference wrapper around the Helper object. It uses the standard compareAndSet functionality to set a newly created Helper object if helperRef is null. Otherwise, it simply returns the already created instance. (Tom Hawtin, JMM Mailing List)

Code Block
bgColor#ccccff

// Uses atomic utilities
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();
  }
}

Compliant Solution (immutable)

In this solution the Foo class is unchanged (it is immutable as before), but the Helper class is immutable. In this case, 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.

...