StackTips
 19 minutes

Implementing Singleton Design Pattern in Java

By Nilanchala @nilan, On Sep 19, 2023 Design Pattern 2.81K Views

Singleton design pattern belongs to the creational family of patterns that governs the object instantiation process.

This pattern ensures at most one instance of a particular class is ever created in your application. Following are some of the real-time examples listed below.

  • Project Configuration: Project configurations are read and loaded into memory once and used multiple times throughout the application lifecycle. Singleton is the best choice to implement that.
  • Application Log: The Logger object will be used globally everywhere in your application. It must be initialized once and used everywhere.
  • Analytics and Reporting: If you are using some kind of data Analytics or tracking service, then you must design them to be singleton so that they are initialized once and used everywhere.


Notice that the Singleton class has a private static instance variable named instance. The default constructor of Singleton class is made private to prevent other class to instantiate it.

The getInstance() method is delcared in the static scope to make the method accessible globally. The getInstance() method returns the singleton object.

Implementing the Singleton Class

To make a class as singleton, you have to:

  1. Make the default constructor private. Private constructor prevents the direct instantiation of the object from other classes.
  1. Create a public static getInstance() method. A member declared as static can be accessed without creating an object using the new operator. This method returns the instance of the singleton class.
  1. Lazy initialization is preferable to create an object on first use.

Singleton with Lazy Initialization

Example: Creating a singleton class using lazy initialization

class Singleton {
	private static Singleton instance;

	private Singleton() {
	}

	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}

	public void method1() {
		System.out.println("Hurray! I am method1 from Singleton!");
	}
}

Let us now test the singleton initialization:

Singleton object = new Singleton();
object.method1();

???The above code will result in compilation error. As the default constructor is made private, you are not allowed to create instance using new keyword. Now let us use the getInstance() method to create the instance of Singleton class.

Singleton object = Singleton.getInstance();
object.method1();

This is how we create a Singleton class using lazy initialization. The getInstance() method instantiating the class for the first use.

Singleton and Thread Safety

The above method of creating singleton class is not thread safe. Two instances of Singleton class will be created if the getInstance() called simultaneously by two threads.

This issue can be avoided by making the getInstance() method synchronized. This way we force every thread to wait for its turn, before it executes. i.e. no two threads can be entered into getInstance() method at the same time.

public static synchronized Singleton getInstance() {
	if (instance == null) {
		instance = new Singleton();
	}
  return instance;
}

The above implementation will answer to the thread safety problem, however, the synchronized methods are expensive and can have a serious performance hit.

We can optimize the above code and add a null check before entering into the synchronization block.

public static synchronized Singleton getInstance() {
	if (instance == null) {	
		synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}	
		}	
	return instance;
}

Singleton with Early Initialization

Using early initialization we will initialize upfront before your class is being loaded. This way you don’t need to check for synchronization as it is initialized before being used ever.

class Singleton {
	private static Singleton instance = new Singleton();

	private Singleton() {
	}

	public static Singleton getInstance() {
		return instance;
	}

	public void method1() {
		System.out.println("Hurray! I am method1 from Singleton!");
	}
}

Singleton and Object Cloning

Java has the ability to create a copy of the object with similar attributes and state form original object. This concept in java is called cloning.

To implement cloning, we have to implement java.lang.Cloneable interface and override clone() method from Object class.

It is a good idea to prevent cloning in a singleton class. To prevent cloning on singleton object, let us explicitly throw CloneNotSupportedException exception in clone() method.

class Singleton implements Cloneable {
	private static Singleton instance;

	private Singleton() {
	}

	public static synchronized Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	public void method1() {
		System.out.println("Hurray! I am method1 from Singleton!");
	}
}

Singleton and Serialization

Serialization in Java allows to convert the state of an object into a stream of bytes so that it can easily be stored or transferred. Once the object is serialized, you can deserialize it, back to object from the byte stream.

If a singleton class is meant to be serialized, it will end up creating duplicate objects. Let us have a look into the example below,

import java.io.Serializable;

class Singleton implements Cloneable, Serializable {
	private static final long serialVersionUID = 1L;
	private static Singleton instance;
	private int value;

	private Singleton() {
	}

	public static synchronized Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	public void method1() {
		System.out.println("Hurray! I am method1 from Singleton!");
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
}

In this example the Singleton class is implementing the java.io.Serializable interface, which means the state of its object can be persisted.

Now, let us save the object into a file and then retrieve it later.

public static void main(String[] args) {
    Singleton instanceOne = Singleton.getInstance();
    instanceOne.setValue(10);

    try {
        FileOutputStream fos = new FileOutputStream("filename.txt")
        ObjectOutput out = new ObjectOutputStream(fos);
        out.writeObject(instanceOne);
        out.close();
        instanceOne.setValue(20);

        FileInputStream fis = new FileInputStream("filename.txt")
        ObjectInput in = new ObjectInputStream(fis);
        Singleton instanceTwo = (Singleton) in.readObject();
        in.close();

        System.out.println("instanceOne:" + instanceOne.getValue());
        System.out.println("instanceTwo:" + instanceTwo.getValue());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

This will print different values, which means that they are two different objects. Here we are violating the singleton principle.

To solve this issue, we need to include readResolve() method in our Singleton class. This method will be invoked before the object is deserialized and here we will call the getInstance() method to return the same object after deserialization.

class Singleton implements Cloneable, Serializable {
	private static final long serialVersionUID = 1L;
	private static Singleton instance;
	private int value;

	private Singleton() {
	}

	public static synchronized Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
  
  protected Object readResolve() {
     return getInstance();
  }

	@Override
	public Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	public void method1() {
		System.out.println("Hurray! I am method1 from Singleton!");
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
}

Noteworthy

  • Singleton classes are used sparingly. Do not think of this pattern, unless you know what you are doing. As the object is created in the global scope, this is riskier in resource-constrained platforms.
  • Beware of object cloning. Double check and block object’s clone method
  • Careful when multiple threads accessing the singleton class
  • Careful of multiple class loaders they can break your singleton
  • Implement strict type if your singleton class is serialized
nilan avtar

Nilanchala

I'm a blogger, educator and a full stack developer. Mainly focused on Java, Spring and Micro-service architecture. I love to learn, code, make and break things.