Sitemap

2. Design Pattern:- Correct way to implement Singleton Pattern

5 min readJul 21, 2023

--

It is one of the simplest creational design patterns. It is used when we need to create just one object of a class.

Press enter or click to view image in full size
Singleton Design Pattern

There are 4 ways to achieve this functionality. We will discuss each of them one by one. Every next approach mitigates the issue with the current approach.

  1. Eager Initialization
  2. Lazy Initialization
  3. Use synchronized with lazy initialization
  4. Use double locking with lazy initialization

Eager Initialization

It is super easy to initialize an object of a class statically. This is known as eager initialization. The object gets instantiated as soon as the process starts.

public class MyObject {

//contructor has to be private to restrict its creation
private MyObject() {}

//eager initialization
private static MyObject object = new MyObject();

public static MyObject getInstance() {
return object;
}
}

This is the easiest method to create a singleton class but it has a drawback, that instance is created even though the client application might not be using it. Lazy initialization comes into the picture to resolve this issue.

Lazy Initialization

Object creation becomes part of the getInstance() method and the object gets instantiated only when the client application needs it.

Press enter or click to view image in full size
We will cross the bridge when we come to it
public class MyObject {

//contructor has to be private to restrict its creation
private MyObject() {}

//lazy initialization
private static MyObject object = null;

public static MyObject getInstance() {
if(object == null) {
object = new MyObject();
}
return object;
}
}

What happens when there are 2 concurrent requests to the getInstance() method?

Get Shivanshu Goyal’s stories in your inbox

Join Medium for free to get updates from this writer.

Both the requests find the null object and as a result, two instances get created for the MyObject class. The next 2 ways will help us to fix this problem.

Use synchronized with Lazy initialization

Synchronized helps us to make our code thread-safe in a multi-threading environment. Attaching synchronized to the getInstance() method does not let multiple concurrent requests enter into the method at the same time.

public class MyObject {

//contructor has to be private to restrict its creation
private MyObject() {}

//lazy initialization
private static MyObject object = null;

public synchronized static MyObject getInstance() {
if(object == null) {
object = new MyObject();
}
return object;
}
}

This looks good. We are sure at this point, only one object can be instantiated for my class. This solution guarantees only one object creation, but does it bring any issues with it? Yes, It does. Now, every request to fetch the instance of MyObject class has to go through locking (synchronized) which is apparently an expensive operation. We will see how double locking mitigates this issue for us.

Use double locking with lazy initialization

To solve the problem with the approach of using the synchronized keyword at the method level, we will be checking for a null object twice, and locking will be applied inside the method.

Double locking
public class MyObject {

//contructor has to be private to restrict its creation
private MyObject() {}

//lazy initialization
private static MyObject object = null;

public static MyObject getInstance() {
if(object == null) { //1st locking or check
synchronized(MyObject.class) {
if(object == null) { //2nd locking or check
object = new MyObject();
}
}
}
return object;
}
}

All the requests which come after the first request get the object directly as the object is not null or created by that time. There is no locking for those requests.

If two threads come concurrently to fetch the object of the class. Both satisfy the first if condition and only one is allowed to enter into the synchronized block and the object is created for that thread only. When the second thread enters the block, the object becomes not null.

Okay. This should be the final version (or approach) to implement the Singleton pattern. Wait… There is still one problem if we go in-depth about memory management.

Caching is the culprit. CPU tends to use cache to optimize its operation’s performance. Let’s take an example to understand it better.

Press enter or click to view image in full size
Two threads on 2 different cores trying to create the object for my class
  1. There could be multiple cores (4 in our case) on your system. Each core will have its own cache to optimize the performance. These caches try to be in sync with each other using an async task. There is a possibility that they are not in sync with each other always.
  2. There are 2 threads T1 and T2 on different cores trying to fetch the object for MyObject class (assume the object is not yet instantiated).
  3. T1 finds there is no object created for MyObject class so it goes and creates an object for that and keeps it in its own cache for some time before letting the main memory know about it.
  4. T2 comes at the same time and checks whether the object is there (cache or main memory) or not. It is not there for T2 so it ends up creating another object of the class.

The solution is to make the object variable volatile. It ensures that the variable is always read from and written to the main memory, rather than from thread-specific caches.

public class MyObject {

//contructor has to be private to restrict its creation
private MyObject() {}

//lazy initialization
private volatile static MyObject object = null;

public static MyObject getInstance() {
if(object == null) { //1st locking or check
synchronized(MyObject.class) {
if(object == null) { //2nd locking or check
object = new MyObject();
}
}
}
return object;
}
}

This is the final version of what we use in the Industry.

Thanks for reading!!

--

--

Shivanshu Goyal
Shivanshu Goyal

Written by Shivanshu Goyal

Software Engineer @Salesforce, USA | Ex-Walmart | Ex-Motorola | Ex-Comviva | Ex-Samsung | IIT Dhanbad