Thread Safe Singleton Design Pattern in Java.

Singleton is a Creational Design Pattern that allows you to only have one object at a time. This object will be static, so a new instance won’t be created every time you retrieve the object. In the singleton pattern, we create a getInstance() method that retrieves an instance of the class for you versus creating an object yourself. I will show you a simple example and how to make this example thread safe.

public class SingletonClass {

    //SingletonClass instance is created at compile time
    private static SingletonClass singletonClass = new SingletonClass();

    public double value;

    //Constructor cannot be created directly because it is private
    //value is set to 0 when class is compiled
    private SingletonClass(){
        value = 0;
    }

    //The Client(Main method) will call getInstance() to get the instance instead of creating an object
    public static SingletonClass getInstance(){
        return singletonClass;
    }
}
In the SingletonClass,  we initialize the singletonClass at compile time since we are setting the variable immediately. We have a value that we can update directly since it is a public variable. We made the constructor private so that the class cannot be instantiated from the client program (In our case, the main method below). You can get the SingleClass instance using SingletonClass.getInstance() versus SingletonClass instance = new SingleClass()

Lets look at the main method.

public class Main {
    public static void main(String[] args) {
      
        SingletonClass singletonClass1 = SingletonClass.getInstance();
        singletonClass1.value += 5;

        System.out.println("SingletonClass1 value: " + singletonClass1.value);

        SingletonClass singletonClass2 = SingletonClass.getInstance();
        singletonClass2.value += 5;

        System.out.println("SingletonClass2 value: " + singletonClass2.value);

        SingletonClass singletonClass3 = SingletonClass.getInstance();
        singletonClass3.value += 5;

        System.out.println("SingletonClass2 value: " + singletonClass1.value);

    }
}

Here we instantiated SingletonClass into 3 seperate variables. Since the instance is static, each variable will contain the same instance. As you can see, we use each variable to increment the value by 5. Since it maintains the same instance, it will print out as 5, 10, 15 instead of 5, 5, and 5.

There are 2 potential issues with this code though.

  1. The instance is not lazily loaded. This means that the instance is created at compile time versus when we need to get the instance. When the class is loaded, the instance is created when we initialize the variable. 
    private static SingletonClass singletonClass = new SingletonClass();

We only want to create the instance when we call getInstance() the very first time. Let’s make this change:
public class SingletonClass {

    //SingletonClass instance is initialized to null at compile time
    private static SingletonClass singletonClass = null;

    public double value;

    private SingletonClass(){
        value = 0;
    }

    public static SingletonClass getInstance(){
        //The singletonClass is initialized when we call getInstance() the very first time.
        if (singletonClass == null){
            singletonClass = new SingletonClass();
        }
        return singletonClass;
    }
}
As you can see, we declared the instance as null instead of a new SingletonClass(). Then we add a null check in the getInstance() method. This will only initialize the instance when call getInstance() the first time (when we actually need it).


      2. This class is not thread safe.

This means that if multiple threads are running and want to call getInstance() at the same time. Then there is a chance that 2 threads will get past the null check and singletonClass = new SingletonClass(); will get called twice.

 public static SingletonClass getInstance(){
        //This synchronized block will not allow 2 threads to get inside. One thread will enter the
        //block while the second thread waits for it to complete.
        if(singletonClass == null){
            synchronized (SingletonClass.class){
                if (singletonClass == null){
                    singletonClass = new SingletonClass();
                }
            }
        }
        return singletonClass;
    }

Here we added a synchronized block that will only allow 1 thread in at a time. 1 thread will wait while another is in the block, checks if the instance is null, and exits. I added a separate null check inside because 2 threads could get past the null check the first time getInstance is called. We also don’t add synchronized outside the null check because we would only have one thread checking if it’s null at a time. This would be detrimental to performance.