Singleton Pattern

  • When we need single instance of a class (shared resource)
  • We need access to that instance from whole application
  • Examples in Java:
  • We cannot keep it as global variable since anyone will be able to overwrite the instance
  • Disadvantages:
    • Never allow parameter otherwise it will become factory pattern
    • Thread safety must be done
    • It is difficult to test
    • Difficult to debug and find which code path lead to singleton’s current state
  • considered anti-pattern these days
  • Many Db driver can not manage same instance under multiple thread. Also singleton pattern have not thread safe structure.
  • Thread-safe Ways to implement
    • Eager loading
    • Lazy loading
      • Synchronized method
      • Double locking
      • Using Holder class
      • Using enums

Implementation

// Eager Loading
class LoggerSingleton {
    // notice static
	private static LoggerSingleton instance = new LoggerSingleton();
	
	// notice private constructor
	private LoggerSingleton() {}
	
	// notice static
	// instance can be accessed only here
	public static LoggerSingleton getInstance() {
		return instance;
	} 
}
  • Lazy Loading: Not Thread Safe
class LoggerSingleton {
	private static LoggerSingleton instance = null;
	private LoggerSingleton() {}
	public static LoggerSingleton getInstance() {
		if (instance == null) {
			instance = new LoggerInstance();
		}
		return instance;
	} 
}
  • Synchronized method: Thread Safe
    • Never used since locking method is expensive
    • Once the instance is created, if a lot of threads call getInstance(), then it is useless to keep the other threads waiting
    • Need to use volatile to prevent visibility problem. See volatile
class LoggerSingleton {
	private static volatile LoggerSingleton instance = null;
	private LoggerSingleton() { }
	synchronized public static LoggerSingleton getInstance() {
		if (instance == null) {
            instance = new LoggerInstance();
        }
		return instance;
	} 
}
  • Single Checked Locking: Not Thread Safe
    • Block synchronized locking is less expensive than method level locking since we only need to synchronize less code
    • Notice that block level locking is not actually executed much, instead the first check itself returns instance if available
    • LoggerSingleton.class is an immutable object of Class<?> which is considered for monitor object
    • In rare scenario, Both threads can enter synchronized block one by one
    • This will lead to two instances being created
class LoggerSingleton {
	private static volatile LoggerSingleton instance = null;
	private LoggerSingleton() { }
 
	public static LoggerSingleton getInstance() {
		if (instance == null) {
    		// Bad: both t1 and t2 come here
    		// and enter one by one inside the following block
			synchronized(LoggerSingleton.class) { 
                instance = new LoggerInstance();
			}
		}
		return instance;
	}
}
  • Double Checked (Single) Locking: Thread Safe
    • This is used in production but it also has some flaws. what flaws??
class LoggerSingleton {
	private static volatile LoggerSingleton instance = null;
	private LoggerSingleton() {
		if (instance != null) {
			throw RuntimeException("Don't try to be smart");
		}
	}
 
	public static LoggerSingleton getInstance() {
		if (instance == null) {
			synchronized(LoggerSingleton.class) {
				if (instance == null) {
					instance = new LoggerInstance();
				}
			}
		}
		return instance;
	}
}

Modern Lazy Initialization Implementation

public class Singleton {
    private Singleton() { }
 
    // static class
    private static class SingletonHolder {
        // static initilizer will be called only once by JVM
        public static final Singleton instance = new Singleton();
    }
 
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
public enum Singleton {
  // JVM will call enum private constructor only once
  INSTANCE;
 
  public void foo() {
      //....
  }
}
 
// access
Singleton.INSTANCE.foo();

Break Singleton Pattern

class Singleton {
    private static volatile instance;
    private Singleton() {
        // To make sure if somebody do not call constructor
		// using reflection
        if (instance != null) {
            // important to throw `RuntimeException`
    		// since it is unchecked exception  
            // otherwise you need to handle it in other places
            throw RuntimeException("Don't try to be smart");
        }
    }
}
  • By Performing locking from outside
class Singleton {
    private static final myLock = new Object();
 
    // ...constructor
 
    public static Singleton getInstance() {
        synchronized(myLock) {
            // code
        }
    }
}
  • Using Serialization
    • You serialize the instance object and deserialize it to create the instance
    • Implement readResolve() and return instance
class Singleton implements Serializable {
    // ...
    protected Object readResolve() {
        return instance;
    }
}
  • Using Cloning
    • Object.clone() can create a clone of object
    • Override clone() and throw CloneNotSupportedException
class Singleton implements Clonable {
    // ....
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
}

Slf4j

@Slf4j
class Test {}
 
// same as below
 
import org.slf4j.*;
class Test {
	private static final Logger log = LoggerFactory.getLogger(Test.class);
}

UML

  • Keep instance and constructor as private
  • only single getter method: getInstance() Singleton_Pattern_UML|300

Why singleton is anti-pattern for db instance?