Nur Rony
Polyglot Programmer, DevOps and Forever Learner

Design Pattern: Singleton

Design Pattern: Singleton

Singleton is a creational design pattern and the easiest of all design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.

Intent

  • Ensure a class has one and only one instance, and provide a global point of access to it.

Class Diagram

Implementation

There are many ways to implement Singleton Design Pattern. I will try to cover all possible ways under different scenarios.

Eager initialization

In eager initialization, the instance of Singleton Object is created at the time of class loading, this is the easiest method to create a singleton class but it has a drawback that instance is created even though client application might not be using it.

Here is the implementation of the static initialization singleton class.

package info.nmrony.designpatterns.singleton;

public class EagerInitializedSingleton {
    
    private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
    
    //private constructor to avoid client applications to use constructor
    private EagerInitializedSingleton(){
        // Prevent creating object form the reflection api.
        if (instance != null) {
            throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
        }
    }

    public static EagerInitializedSingleton getInstance(){
        return instance;
    }
}

There is an another variant of eager initialization with using static block

package info.nmrony.designpatterns.singleton;

public class StaticBlockSingleton {

    private static StaticBlockSingleton instance;
    
    private StaticBlockSingleton() {
        // Prevent creating object form the reflection api.
        if (instance != null) {
            throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
        }
    }
    
    //static block initialization for exception handling
    static {
        try{
            instance = new StaticBlockSingleton();
        } catch(Exception e) {
            throw new RuntimeException("Exception: creating singleton instance");
        }
    }
    
    public static StaticBlockSingleton getInstance() {
        return instance;
    }
}

💡We should always avoid Eager Initialization of Singleton implementation unless it is a boot time object or application configuration

Lazy Initialization

Lazy initialization method to implement Singleton pattern creates the instance in the global access method. Here is the sample code for creating Singleton class with this approach.

package info.nmrony.designpatterns.singleton;

public class LazyInitializedSingleton {

    private static LazyInitializedSingleton instance;
    
    private LazyInitializedSingleton() {
        // Prevent creating object form the reflection api.
        if (instance != null) {
            throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
        }
    }
    
    public static LazyInitializedSingleton getInstance(){
        if(instance == null){
            instance = new LazyInitializedSingleton();
        }
        return instance;
    }
}

💣 The Singleton Class shown above works fine in single-threaded environment.

Thread-Safe Singleton

The easier way to create a thread-safe singleton class is to make the global access method synchronized, so that only one thread can execute this method at a time. General implementation of this approach is like the below class.

package info.nmrony.designpatterns.singleton;

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;
    
    private ThreadSafeSingleton() {
        // Prevent creating object form the reflection api.
        if (instance != null) {
            throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
        }
    }
    
    public static synchronized ThreadSafeSingleton getInstance(){
        if(instance == null){
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
    
}

There is a trade-off in above implementation. Although it provides thread safety but it reduces the performance and might impact on overall application performance if this Singleton class is accessed by large number of thread. To make the performance impact sane double checked locking principle is used. In this approach, the synchronized block is used inside the if condition with an additional check to ensure that only one instance of a singleton class is created.

package info.nmrony.designpatterns.singleton;

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;
    
    private ThreadSafeSingleton() {
        // Prevent creating object form the reflection api.
        if (instance != null) {
            throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
        }
    }
    
    public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){
        if(instance == null){
            synchronized (ThreadSafeSingleton.class) {
                if(instance == null){
                    instance = new ThreadSafeSingleton();
                }
            }
        }
        return instance;
    }
    
}

Saving Singleton State

Sometimes we need to save Singleton object state (ie: In distributed system). For this we need to implement Serializable Interface. But Deserialization process creates new instance which is violation of Singleton Pattern. To avoid this we just add this following method in class.

protected Object readResolve() {
    return getInstance();
}

🛎 Singleton is mostly considered an anti-Pattern, because it brings inherent complexity to the system in terms of testing. Only DI frameworks (Spring, Dagger, etc) should be allowed to create Singletons for you rather than you writing the singletons. However, if it is absolute essential to write your own Singleton, then you must write it correctly.