All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.concurrentli.Singleton Maven / Gradle / Ivy

/*
 * Copyright 2017 LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
 * See LICENSE in the project root for license information.
 */

package com.concurrentli;

import java.io.Serializable;
import java.util.function.Supplier;


/**
 * Provides a singleton using efficient, thread-safe double-checked locking.
 *
 * Use of Singleton can be much faster than a traditional double-checked locking singleton implementation using
 * a volatile, although it's worth noting that a volatile read on x86 is no more expensive than a normal read from
 * memory except that many optimizations are disallowed; the performance advantage of Singleton is thus dependent
 * on Java being able to take advantage of optimizations like "lifting" the read of the singleton out of a loop.
 *
 * Nonetheless, the best way to implement a singleton is through the use of a final field or an enum.  Use this class
 * only when neither of these is an option.
 *
 * To use, create a derived class and implement the getValue() method that returns a new singleton instance
 * when needed.
 *
 * To avoid the cost of the Singleton object, you may also call the static getFullyConstructed(...) method
 * directly.
 *
 * Note that, in practice, naive (and incorrect) use of double-checked locking to create singletons is likely
 * to still work, particularly on x86 machines (which have strong memory ordering guarantees).  This makes
 * it hard, as a pratical matter, to empirically test an implementation for correctness.
 *
 * Adapted from https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
 *
 * @param  the type of object provided as a singleton.
 */
public abstract class Singleton implements Supplier, Serializable {
  private static final long serialVersionUID = 1;

  /**
   * Uses a constructor with a final field to ensure that (in effect) a StoreStore memory barrier is enforced
   * before returning a reference to the passed argument.  So long as the passed object has not leaked previously,
   * any subsequent reads of the returned reference will see the "full" object and not, e.g. something partially
   * constructed.  This is effectively a no-op on x86 due to its already strong memory ordering guarantees.
   *
   * Note that writes and reads of references are always atomic in Java.
   *
   * A double-checked lock singleton pattern can thus look like:
   * public ClassName get() {
   *   ClassName s = _singleton; // need temp variable because reads can be reordered
   *   if (s != null) {
   *     return s; // guarantee to point to complete object, if not null
   *   }
   *
   *   synchronized (this) {
   *     if (_singleton == null) {
   *       _singleton = Singleton.getFullyConstructed(new ClassName());
   *     }
   *     return _singleton;
   *   }
   * }
   *
   * @param obj the object you wish to be completely visible to all threads
   * @param  the type of the object
   * @return a reference to the object that will appear as a correct and complete instance to other threads
   */
  public static  T getFullyConstructed(T obj) {
    return new FinalWrapper<>(obj).value;
  }

  private static final class FinalWrapper {
    final T value;
    FinalWrapper(T value) {
      this.value = value;
    }
  }

  private transient T _obj = null;

  protected abstract T getValue();

  public final T get() {
    // read from memory
    T temp = _obj;
    if (temp != null) {
      return temp;
    }

    synchronized (this) { // synchronize to avoid calling supplier twice
      if (_obj == null) { // is _obj still null?  Maybe someone calculated it for us already.
        _obj = getFullyConstructed(getValue()); // create _obj, write to memory
      }
      return _obj;
    }
  }

  /**
   * Clears the value stored in the singleton.  Other threads may continue seeing the old value until they
   * synchronize with the calling thread (i.e. establish a happens-before relationship).
   */
  public void clear() {
    synchronized (this) {
      _obj = null;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy