Singleton Design Pattern in Java

Singleton Design Pattern in Java

Singleton is a creational design pattern that lets you ensure that a class has only one instance.

Singleton design pattern allows us to create only one instance of a class and make sure that there exists only one Object in JVM.

How to achieve it?

1.private static variable of the same class.

2.private constructor to instantiate the object of the class.

3.public static method to get the instance of the class.

Eager Initialization :

class Hello{
    //Only object created when class is loaded and theInstance is private static var pointing to it.
    private static Hello h = new Hello();
    private Hello(){
        //Making it private so that other class can't access it.
        System.out.println("Calling Hello!");
    }
    //public method to return single instance of class.
    public static Hello getInstance(){
        return h;
    }
}

Problems:

The object will always be created no matter whether it is required or not and hence there will be a wastage of heap memory in case we don’t need the instance of this singleton class.

Lazy Initialization :

In the case of the lazy initialization approach, the object of the class is created when the getInstance() method is called.

class Hello{
    //private static variable of same class
    private static Hello h = null;
    private Hello(){
        //Making it private so that other class can't access it.
        System.out.println("Calling Hello!");
    }
    //public method to return single instance of class.
    public static Hello getInstance(){
        if(h == null){
            h = new Hello();
        }
        return h;
    }
}

Problems:

In the case of a multi-threading environment, multiple threads can be inside the if block at the same time and they may create multiple objects. So it will violate the rule of the Singleton design pattern.

Like -

public class Singleton1 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Hello h = Hello.getInstance();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Hello h = Hello.getInstance();
            }
        });

        t1.start();
        t2.start();
    }
}

If we use 2 thread to create the instance it violets the singleton design pattern

As the threads are not synchronized they call the getInstance() Method same time and try to create the instance at the same time.

Thread Safe Singleton :

To overcome the disadvantage of lazy initialization we can have a synchronized getInstance() method so that only one thread will be able to enter the method at a time.

class Hello{
    //private static variable of same class
    private static Hello h = null;
    private Hello(){
        //Making it private so that other class can't access it.
        System.out.println("Calling Hello!");
    }
    //public method to return single instance of class.
    public static synchronized Hello getInstance(){
        if(h == null){
            h = new Hello();
        }
        return h;
    }
}

Problems:

As the method is synchronized, only one thread at a time will be able to access the getInstance() method at any given time because there will be only one lock available per object that is shared between the threads. Hence it will reduce performance.

Lazy Initialization with Double-Check Locking :

This approach guarantees us that only one instance will be created at any given point in time inside the memory. As the if condition is synchronized, it will be less overhead than the synchronized method.

class Hello{
    //private static variable of same class
    private static Hello h = null;
    private Hello(){
        //Making it private so that other class can't access it.
        System.out.println("Calling Hello!");
    }
    //public method to return single instance of class.
    public static  Hello getInstance(){
        if(h == null){
            synchronized(Hello.class) {
                //only one thread will be able to access this block.
                if(h ==  null){
                    //once the object is created by one thread ,
                    // for other threads this if condition will be false.
                    h = new Hello();
                }
            }
        }
        return h;
    }
}