Some Other ways of implementing the Singleton Pattern

In this post, we will see some more ways to implement a Singleton pattern.

First Solution: Using ReentrantReadWriteLock

public final class Singleton {

  private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();

  private static Singleton instance;

  /* Private constructor to prevent instantiation by external classes */
  private Singleton() {}

  public static Singleton getInstance() {
    LOCK.readLock().lock();
    if (instance == null) {
      LOCK.writeLock().lock();
      instance = new Singleton();
      LOCK.writeLock().unlock();
    }
    try {
      return instance;
    } finally {
      LOCK.readLock().unlock();
    }
  }
}

Second solution: Using Object as a Lock.

public final class Singleton {

  private static final Object mutex = new Object();

  private static Singleton instance;

  /* Private constructor to prevent instantiation by external classes */
  private Singleton() {}

  public static Singleton getInstance() {
    // almost no cost to verify here
    if (instance == null) {
      synchronized(mutex) {
        // double check if instance is still null
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

Third Solution: Using separate read and write locks.

public final class Singleton {

  private static final Object readLock = new Object();
  private static final Object writeLock = new Object();

  private static Singleton instance;

  /* Private constructor to prevent instantiation by external classes */
  private Singleton() {}

  public static Singleton getInstance() {
    synchronized(readLock) {
      if (instance == null) {
        synchronized(writeLock) {
          if (instance == null) {
            instance = new Singleton();
          }
        }
      }
      return instance;
    }
  }
}
Creational-Pattern Core Java