Creational Design Pattern Part 1: Singleton

Reading Time: 3 minutes

Overview

In this article, I will be sharing the working of a singleton pattern, in a nutshell, using a simple real-world example.

1. Singleton Design Pattern

The aim of the singleton pattern is on the initialization of an object of a particular class by ensuring that one instance of the object exists throughout Java Virtual Machine. It was introduced by GoF, although the original implementation is a bit problematic in multi-threaded scenarios.

1.1. Singleton Pattern Example

Let’s try to understand why it was discovered by looking at the following scenario:-

Problem Statement – Let’s take a classic scenario where you would like to take multiple prints from different applications like Word, Excel, PDF, etc simultaneously. And you click the PRINT button for all of them at the same instant. What will happen in this scenario?

A separate object of the printer will be created for every application. All of them would try to access the shared resource and might happen that one page of the word gets printed and the printer might start printing pages of different applications. The behavior of the printer is unpredictable in this case unless we give a lock to every object.

Now, here we can see the necessity of having one object per resource. How can we achieve that?

Singleton Patterns gives us the capability of creating ONE OBJECT AT A TIME.

public class TestLogger {
public static void main(String args[]) {
Logger logger1 = Logger.getInstance();
Logger logger2 = Logger.getInstance();
TestLogger testLogger1 = new TestLogger();
TestLogger testLogger2 = new TestLogger();
System.out.println(logger1.hashCode());
System.out.println(logger2.hashCode());
System.out.println(testLogger1.hashCode());
System.out.println(testLogger2.hashCode());
}
}
view raw gistfile1.txt hosted with ❤ by GitHub

1625635731
1625635731
1580066828
491044090

 

Implementation

This implementation clearly describes the printer example that was explained above.

package Singleton;
public class Printer {
// Private – It should be accessed by only getInstance Methods()
private static Printer instance;
private Printer() {
// Private Constructor to make sure that no more than one instance is created at a given time.
}
// Double check Locking
public static Printer getInstance() {
if (instance == null) {
// This block creates instance so that min threads wait and only for first time.
synchronized (Printer.class) {
if (instance == null) {
instance = new Printer();
}
}
}
return instance;
}
void print(Object obj) {
if (obj instanceof MSWord) {
MSWord wordInstance = (MSWord) obj;
System.out.println("I am Printing " + wordInstance.page);
} else {
AcrobatReader pdfInstance = (AcrobatReader) obj;
System.out.println("I am Printing " + pdfInstance.page);
}
}
}
view raw gistfile1.txt hosted with ❤ by GitHub

When you create a constructor as private, an object outside of that class cannot be created. How do you create an object in this situation? You can create an instance of an object by creating a method that calls “NEW” if no instance of an object exists. Now, why making this method and instance static? Well, you know that static methods can be called without instantiating the class. Also, they can only use static variables inside them. So this the reason why we must make the name of instance static.

There are multiple ways to create a singleton instance of a class. I am trying to illustrate Lazy Initialization with a double-check locking mechanism through an above-defined piece of code. With the help of double-check locking, we are trying to make a check whether the printer instance is already created or not.

The first time, all the threads make a simple check of whether the instance is null or not. In the case of NULL, it acquires a lock on the class, so that no other threads enter the block at the same time. Second-time check, we are making sure that no other thread has created an instance. Also, making a check on whether the current thread has not created an instance.

Class Level Locking comes into picture when we want to make our static data members thread-safe. Now relating it to this scenario, the static instance that we have created is thread-safe. We want only one thread to execute a block of code that creates a new OBJECT at a time.

Continuing with the below code we can access this simple scenario :-

package Singleton;
public class MSWord {
String page = "Word Page";
public static void main(String args[]) {
MSWord wordObj = new MSWord();
Printer instance = Printer.getInstance();
instance.print(wordObj);
}
}
package Singleton;
public class AcrobatReader {
String page = "Pdf Page";
public static void main(String args[]) {
AcrobatReader pdfObj = new AcrobatReader();
Printer instance = Printer.getInstance();
instance.print(pdfObj);
}
}
view raw gistfile1.txt hosted with ❤ by GitHub

When to Use Singleton Design Pattern?

  • For expensive resources like database connections or shared resources like printer etc.
  • It’s a good practice to keep all loggers as Singleton.
  • When you require a unique global point of access which returns a particular object at a particular point of time.

References

Many thanks for reading this blog! In the next blog, I will discuss another design pattern with some real-world examples. Till then, Stay Tuned 🙂 Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.

blog-footer