Singleton Design

Singleton Design Pattern Using Java

4398 VIEWS

·

A design pattern is a step-by-step solution to a problem. If followed appropriately, it can significantly help us reduce the pain that comes up with managing objects.

There are numerous design patterns in Java, which fit into four categories.

  1. Creational Design pattern
  2. Structural Design pattern
  3. Behavioural Design Pattern
  4. Design patterns exclusively for J2EE.

Here, we will discuss the Singleton Pattern, which is part of the Creational Design Pattern.

What is the Singleton Pattern?

It ensures that only one instance of the class resides in the entire lifecycle of application, which ensures that instantiation of heavy objects is done only once.

When to use this pattern

  1. Use this when you have a class whose object, when created, takes up more resources every time, and generally creates a time-consuming process. Consider, for example, the class responsible for networking (Retrofit in Android) or the class responsible for managing database connections. Initialization of these types of objects takes up more memory and resources, and they are generally considered as heavy objects. We should ensure that these objects are not instantiated every time—otherwise they will end up taking most of the resources available.
  2. If we ensure that only one instance of these classes remains in the system, our resources won’t be wasted every time we access these resources.

How to apply the Singleton pattern

There are several approaches to this. Let’s consider the most simple approach:

Eager initialization approach:

class EagerSingleton {
	private static EagerSingleton singletonInstance = new EagerSingleton();

	public static EagerSingleton getInstance() {
    		return singletonInstance;
	}
}

You can call EagerSingleton.getInstance() to get the Singleton instance.

Though this approach will work for simpler cases, it doesn’t provide exception handling, which is required while dealing with networking and database connections.

Lazy initialization approach:

class LazySingleton {

	// singleton instance
	private static LazySingleton lazySingleton = null;

	/**
	* private constructor so that other classes can't
	* instantiate it through constructor
	*/
	private LazySingleton() {
    	// all initialization steps go here
	}

	public static LazySingleton getInstance() {
    	if(lazySingleton == null) {
        	lazySingleton = new LazySingleton();
    	}

    	return lazySingleton;
	}
}

With this approach, initialization happens when the client calls getInstance().
Initialization is only going to happen once because lazySingleton will only be null the first time. We can easily handle exceptions in this case.

However, this method is not thread safe because multiple instances might get created when multiple threads are trying to access this method in a multithreading environment.

Thread safe approach:

Lazy initialization implementation was not thread safe. However, this can be easily avoided using a “synchronized” keyword in Java.

class ThreadSafeSingleton {
	private static ThreadSafeSingleton threadSafeSingleton;

	/**
	* private constructor so that other classes can't
	* instantiate it through constructor
	* any instantiation while creating new singleton instance should come here
	*/
	private ThreadSafeSingleton() {}

	public static synchronized ThreadSafeSingleton getInstance() {
    	if(threadSafeSingleton == null) {
        	threadSafeSingleton = new ThreadSafeSingleton();
    	}

    	return threadSafeSingleton;
	}
}

Using a synchronized keyword at method level ensures that only one thread accesses it at a particular time, and that multiple instances aren’t created every time. However, using a synchronized keyword comes with performance cost. So each time a method with a synchronized keyword gets called, we have to incur some amount of performance cost.

Thread safe with double-checked locking:

Earlier performance costs can be easily overcome if, instead of using a synchronized keyword at method level, we use it inside the “if” condition. This way, performance cost will only occur when an instance is being created for the first time. Using a synchronized keyword in an optimal way will transform into a huge performance difference. This is known as a “double-checked locking” mechanism.

Let’s see how we can use the synchronized keyword inside the “if” condition.

class ThreadSafeSingletonWithDoubleCheckedLocking {
	private static ThreadSafeSingletonWithDoubleCheckedLocking threadSafeSingleton;

	/**
	* private constructor so that other classes can't
	* instantiate it through constructor
	* any instantiation while creating new singleton instance should come here
	*/
	private ThreadSafeSingletonWithDoubleLocking() {}

	public static synchronized ThreadSafeSingletonWithDoubleCheckedLocking getInstance() {
    	if(threadSafeSingleton == null) {
        	synchronized(ThreadSafeSingletonWithDoubleCheckedLocking.class) {
            	threadSafeSingleton = new ThreadSafeSingletonWithDoubleCheckedLocking();
        	}
    	}

    	return threadSafeSingleton;
	}
}

It ensures that instance creation goes into thread safety mode only when required, reducing performance cost.

There are several other ways to implement the Singleton design pattern. However, the above- mentioned ways are the most preferred.

Feel free to comment. Share your thoughts or any questions you have. I’ll reply as soon as I can.


Suraj Dubey is a software developer and has extensive experience in mobile app development. He believes in adhering to best practices while writing code, and is willing to help anyone interested in learning and improving.


Discussion

Click on a tab to select how you'd like to leave your comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Menu
Skip to toolbar