You are here
Home > java >

Singleton Design Pattern in Java with all Scenarios

Singleton Design Pattern In Java With All Scenarios In this article, we will go through all the scenarios of the Singleton pattern to make it crystal clear. From the definition, it seems to be a very simple design pattern but when it comes to implementation, it creates a lot of implementation concerns. Also, the implementation of Java Singleton pattern has always been a controversial topic among developers. Here, we will learn about Singleton Design Pattern in Java with all Scenarios, different ways to implement Singleton design pattern and some of the best practices for its usage.

Sometimes, we get the requirement for some classes to have exactly one instance. There are many occasions when we need only one instance of the Object and if we instantiate more than one, we’ll run into all sorts of problems like incorrect program behavior, overuse of resources, or inconsistent results. In this article, we will learn about ‘Singleton Design Pattern in Java with all Scenarios’ in detail and its related concepts.

Why & When to use the Singleton Design Pattern?

The Singleton Pattern puts restriction on the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine. The singleton class must provide a global access point to get the instance of the class. Singleton pattern is generally useful when the object that is created once and shared across different threads/applications. If in your solution some object has only one instance and you want to model that in your design then you should use singleton pattern.

Some of the usage of the Singleton pattern are in thread pool, logging, caching, driver objects etc. We can even see the usage of Singleton design pattern in core Java API classes also. For example, java.lang.Runtime, java.awt.Desktop etc.

What is a Singleton Design Pattern?

There are only two points in the definition of a singleton design pattern,

1) There should be only one instance allowed for a class and
2) We should allow global point of access to that single instance.

How to implement a Singleton Design Pattern in general?

In order to implement a Singleton pattern, we have different scenarios, but each of them has the following common approach.

  • Private constructor to restrict instantiation of the class from other classes.
  • Private static variable of the same class that is the only instance of the class.
  • Public static method that returns the instance of the class, this is the global access point for outer world to get the instance of the singleton class.

Furthermore, we will discuss different scenarios of Singleton pattern implementation and design concerns with the implementation.

Now, let’s discuss Singleton Design Pattern in Java with all Scenarios thoroughly.

Singleton Design Pattern in Java with all Scenarios & Examples

When it comes to implement Singleton Design Pattern, variety of scenarios come into picture. Let’s discuss them one by one in detail.

Thread-Safe Singleton (Eager Initialization)

In eager initialization, an instance is created at the time of class loading. We will not have any thread safety issue if we choose to go with eager initialization. If your application always creates and uses an instance of the Singleton or the overhead of creation and runtime aspects of the Singleton are not burdensome, you may want to create your Singleton eagerly, like this:

Thread Safe-Singleton : Eager Initialization
public class Singleton{

//Eager Initialization
//Below instance is guaranteed to be thread safe.

   private static Singleton uniqueInstance= new Singleton();

   private Singleton() { }

   public static Singleton getInstance() {

//We have already got an instance, so just return it.

    return uniqueInstance;

}

}

Using this approach, we rely on the JVM to create the unique instance of the Singleton when the class is loaded. The JVM guarantees that the instance will be created before any thread accesses the static uniqueInstance variable. Now here is one catch! We have to think about thread safety. In that case, we go with lazy initialization.

*No worries! We will learn about lazy initialization as well in a while.

There are some examples where Singleton pattern violation situation can be found in a thread-safe environment. However, we will use the hash code comparison technique to verify the equality of two objects. If two objects are equal, they MUST have the same hash code.

Singleton Violation on Using Reflection

Using reflection, we can set the private constructor to become accessible at runtime as shown in the example below.

Singleton Violation
package com.dev.dp.creational.singleton;

import java.lang.reflect.Constructor;

public class SingletonR {

public static SingletonR instance= new SingletonR();

private SingletonR() {
System.out.println("creating instance.....");

}

public static SingletonR getInstance() {
return instance;
}

public static void main(String[] args) throws Exception{
SingletonR s1 = SingletonR.getInstance();
SingletonR s2 = SingletonR.getInstance();
System.out.println("Hashcode of Object s1: " +s1.hashCode());
System.out.println("Hashcode of Object s2: " +s2.hashCode());

Class clazz = Class.forName("com.dev.dp.creational.singleton.SingletonR");
Constructor<SingletonR> ctr= clazz.getDeclaredConstructor();
ctr.setAccessible(true);
SingletonR s3 = ctr.newInstance();
System.out.println("Hashcode of Object s3: " +s3.hashCode());

}

}

Output :

Output
creating instance.....
Hashcode of Object s1: 366712642
Hashcode of Object s2: 366712642
creating instance.....
Hashcode of Object s3: 1829164700

From the above output it is clear that using reflection singleton violation can happen.

How to Fix Violation

Throw Runtime Exception if someone tries to make instance, in case one instance already exists. Below code will go into the private constructor of the Singleton class accordingly.

Constructor code of class SingletonR
private SingletonR() {
System.out.println("creating instance.....");
if(instance != null) {
throw new RuntimeException("Can't create instance. Please use getInsance() to create it.");
}
}

Output After fix

Output After Fix
creating instance.....
Hashcode of Object s1: 366712642
Hashcode of Object s2: 366712642
creating instance.....
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.dev.dp.creational.singleton.SingletonR.main(SingletonR.java:29)
Caused by: java.lang.RuntimeException: Can't create instance. Please use getInsance() to create it.
at com.dev.dp.creational.singleton.SingletonR.<init>(SingletonR.java:12)
... 5 more

Singleton Violation on Object Cloning

If we try to make an instance by cloning it, the generated hash code of cloned copy doesn’t match with the actual object so it also violates the Singleton principle.

Singleton Violation
package com.dev.dp.creational.singleton;

public class SingletonC implements Cloneable{

public static SingletonC instance= new SingletonC();

private SingletonC() {
System.out.println("creating instance.....");
}

public static SingletonC getInstance() {
return instance;
}

protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) throws Exception{
SingletonC s1 = SingletonC.getInstance();
SingletonC s2 = SingletonC.getInstance();
System.out.println("Hashcode of Object s1: " +s1.hashCode());
System.out.println("Hashcode of Object s2: " +s2.hashCode());

SingletonC s3 = (SingletonC)s2.clone();
System.out.println("Hashcode of Object s3: " +s3.hashCode());

}
}

Output:

Output
creating instance.....
Hashcode of Object s1: 366712642
Hashcode of Object s2: 366712642
Hashcode of Object s3: 1829164700

How to Fix Violation

Throw CloneNotSupportedException from the clone () method if someone tries to make another instance of it. Add below code in clone() method of above SingletonR class.

Updated code of clone() method
protected Object clone() throws CloneNotSupportedException {
if(instance != null) {
throw new CloneNotSupportedException("Can't create instance. Please use getInsance() to create it.");
}
return super.clone();
}

Output After fix

Output After Fix
creating instance.....
Hashcode of Object s1: 366712642
Hashcode of Object s2: 366712642
Exception in thread "main" java.lang.CloneNotSupportedException: Can't create instance. Please use getInsance() to create it.
at com.dev.dp.creational.singleton.SingletonC.clone(SingletonC.java:17)
at com.dev.dp.creational.singleton.SingletonC.main(SingletonC.java:28)

Singleton Violation on Serialization/Deserialization

When we serialize an object and deserialize it again there are different hash code values generated as shown in the example below. Therefore, our Singleton principle breaks in case of object serialization/deserialization also.

Singleton Violation
package com.dev.dp.creational.singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SingletonS implements Serializable{

public static SingletonS instance= new SingletonS();

private SingletonS() {
System.out.println("creating instance.....");
}

public static SingletonS getInstance() {
return instance;
}

public static void main(String[] args) throws Exception{
SingletonS s1 = SingletonS.getInstance();
SingletonS s2 = SingletonS.getInstance();
System.out.println("Hashcode of Object s1: " +s1.hashCode());
System.out.println("Hashcode of Object s2: " +s2.hashCode());

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/tmp/s2.ser"));
oos.writeObject(s2);

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/tmp/s2.ser"));
SingletonS s3= (SingletonS)ois.readObject();

System.out.println("Hashcode of Object s3: " +s3.hashCode());

}
}

Output 

Output
creating instance.....
Hashcode of Object s1: 2055281021
Hashcode of Object s2: 2055281021
Hashcode of Object s3: 772777427

How to Fix Violation

Implement a new readResolve () method in the Singleton class as shown below.

readResolve( ) method
private Object readResolve() {
System.out.println("Applying readResolve()......");
return SingletonS.getInstance();
}

Output After fix

Output After Fix
creating instance.....
Hashcode of Object s1: 2055281021
Hashcode of Object s2: 2055281021
Applying readResolve()......
Hashcode of Object s3: 2055281021

Singleton in Multithreaded Environment

Singleton will work properly in multi-threaded environment only if eager instantiation has been done because in this case instance creation will happen at the time of class loading only. But for Lazy instantiation we will have to take care of multiple things. If we want to delay the instantiation because of cost, we will have to go with lazy.

Lazy vs Eager Initialization:

Lazy initialization will be beneficial when we want to delay the initialization until it is not needed. On the other hand, if we use eager initialization and if initialization fails, there is no chance to get the instance further while in lazy initialization we may get it in second chance. In Lazy initialization we will not get instance until we call the getInstance method while in eager initialization, it creates instance at the time of class loading itself.

Following code demonstrates the behavior of Singleton instance, when two threads are getting executed by comparing their hash code values. Be careful while running the following code as it will work only in Java 8 and later versions. Moreover, we have used Method Reference in the code.

Singleton in Lazy Initialization
package com.dev.dp.creational.singleton;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SingletonT {

private static SingletonT instance=null; //lazy initialization

private SingletonT(){
System.out.println("Creating...");
}

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

static void useSingleton(){
SingletonT singleton = SingletonT.getInstance();
System.out.println("Hashcode of Singleton Object: "+singleton.hashCode());
}

public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(SingletonT::useSingleton);
service.submit(SingletonT::useSingleton);
service.shutdown();
}
}

Output 

Output
//Output On running first time

Creating...
Object : singleton, Hashcode: 1598725298
Object : singleton, Hashcode: 1598725298

//Output On running second time

Creating...
Creating...
Object : singleton, Hashcode: 1598725298
Object : singleton, Hashcode: 2124340618

//Output On running third time

Creating...
Object : singleton, Hashcode: 1598725298
Object : singleton, Hashcode: 1598725298

Synchronizing method

As shown by the output of the program, It is noticeable that in multithreaded environment sometimes Singleton principle works while sometimes it violates as multiple threads are trying to create instance. Therefore, we need to synchronize the getInstance () method as shown below.

Synchonized getInstance() method
public static synchronized SingletonT getInstance(){
if (instance == null) {
instance = new SingletonT();
}
return instance;
}

Synchronizing block of code

After applying synchronize keyword in the getInstance () method the program may execute properly without any issue, but in Java instead of synchronizing whole method we can synchronize only the block of code which is affected while creating instance to escape the extra overhead as below.

synchronized block of code
public static SingletonT getInstance(){
if (instance == null) {
synchronized (SingletonT.class) {
instance = new SingletonT();
}
}
return instance;
}

Double Checked Locking

Now from the above code we have narrowed down the scope of synchronization for performance reasons. Notice that if a thread at line # 2 notices that instance is null and then it gets the lock of object at line # 3. At the same time if another thread already has the lock it will create the instance. So to make sure no other thread has already acquired the lock we will apply one more check after acquiring the lock as shown below. This technique is called Double Checked Locking. With double-checked locking, we first check to see if an instance is created, and if not, then we synchronize. This way, we only synchronize the first time through, just what we want.

Double Checked Locking
public static SingletonT getInstance(){
if (instance == null) { //check1
synchronized (SingletonT.class) {
if (instance == null) { //check2
instance = new SingletonT();
}
}
}
return instance;
}

In rare cases, double-checked locking also breaks the Principle of Singleton.

Java runtime publishes half initialized variable. To illustrate, suppose 2 threads thread1 & thread2 are entering into the code it goes through the line # 2 to line # 31 and created the instance. Furthermore, at the same time thread 2 enters and it knows that there is something in variable named as ‘instance’ (since it is at half initialized state) and it returns the same from line # 9. Therefore, Singleton principle breaks.

***Note : Double-checked locking doesn’t work in Java 1.4 or earlier! Unfortunately, in Java version 1.4 and earlier, many JVMs contain implementations of the volatile keyword that allow improper synchronization for double-checked locking. If you must use a JVM earlier than Java 5, consider other methods of implementing your Singleton. Although I guess nobody might be using JVM earlier than Java5 but I have mentioned this point for your knowledge purpose only. Also, you may take it as a point of improvement in Java 5 enhancements.

Using volatile keyword

To address above situation, use volatile keyword at the time of instance declaration. Value of volatile variable will be published only when the change completes. Change to write operation happens before read operation in volatile variable. In fact, all threads will see the same value of variable.

Use of volatile Keyword
private static volatile SingletonT instance=null; //lazy initialization

If performance is an issue in using getInstance() method, then this method of implementing the Singleton can drastically reduce the overhead.

Holder Class (Bill Pugh Method) 

Bill Pugh came up with a different approach to create the Singleton class using an inner static helper class as below.

Bill Pugh Singleton
package com.dev.dp.creational.singleton;

public class SingletonH {

private SingletonH() {
System.out.println("Creating.....");
}

static class Holder{
static final SingletonH INSTANCE= new SingletonH(); //lazy

}

public static SingletonH getInstance() {
return Holder.INSTANCE;
}

public static void main(String[] args) {

SingletonH s1= SingletonH.getInstance();
SingletonH s2= SingletonH.getInstance();
SingletonH s3= SingletonH.getInstance();

System.out.println("Hashcode of Object s1: " +s1.hashCode());
System.out.println("Hashcode of Object s2: " +s2.hashCode());
System.out.println("Hashcode of Object s3: " +s3.hashCode());

}

}

Output

Output
Creating.....
Hashcode of Object s1: 366712642
Hashcode of Object s2: 366712642
Hashcode of Object s3: 366712642

As shown from the output, this way of creating Singleton instance doesn’t violates Singleton Principle.

Enum Singleton

Joshua Bloch suggests the use of Enum to implement Singleton design pattern in Java as Java ensures that any enum value is instantiated only once in a Java program. Since Java Enum values are globally accessible, so is the singleton. The drawback is that the enum type is somewhat inflexible; for example, it does not allow lazy initialization.

Enum Singleton
package com.dev.dp.creational.singleton;

public enum EnumSingleton {

INSTANCE;

public static void doMorething() {
//do More thing
}
}

Enum Singleton doesn’t violate principle of Singleton in any case described above.

FAQ

When should we use the Singleton Design Pattern in Java?

Singleton can be useful when we need to ensure that a class has only one instance and that instance can be accessed globally. It is commonly used for logging, database connections, thread pools, and configuration settings.

How do you implement the Singleton Design Pattern in Java?

The Singleton pattern can be implemented by making the constructor of the class private, providing a static method to access the singleton instance, and lazily initializing the instance if necessary.

What are the advantages of using the Singleton Design Pattern in Java?

  • Ensuring a single instance of a class.
  • Providing a global point of access to that instance.
  • Lazy initialization, reducing resource consumption.
  • Thread safety when implemented appropriately.

Are Singleton classes thread-safe?

Singleton classes may or may not be thread-safe, depending on how they are implemented. Synchronization or other mechanisms may be needed to ensure thread safety in multi-threaded environments.

Can we subclass a Singleton class in Java?

Subclassing a Singleton class in java can be challenging and may lead to unexpected behavior. It is generally not recommended to subclass a Singleton class.

Is Singleton pattern anti-pattern?

In some cases, the Singleton pattern can be misused and lead to tight coupling and global state. When used appropriately, it can be a valuable tool for managing resources and ensuring consistency.

Can Singleton Design Pattern in Java lead to memory leaks?

Improper implementation of the Singleton pattern, such as holding onto references indefinitely or using eager initialization unnecessarily, can sometimes lead to memory leaks. It is important to implement the pattern carefully to avoid such issues.

Summary of Singleton Design Pattern in Java

Finally, we learned all about “Singleton Design Pattern in Java with all Scenarios” in a detailed manner. Specifically, we have touched almost all the ways to create Singleton object in this article. If we find any more ways, we will update the article in the future accordingly. Further, if you find any other way or any improvement on the whole article, please feel free to comment or send the email for our review & update.

Apart from Singleton Design Pattern In Java, if you are interested to learn other Design Patterns of GoF, kindly visit article on ‘Design Patterns in Java‘. Furthermore, If you want to know good books on Java Design Patterns, kindly visit the separate article ‘Java Design Patterns Book’.

Links to Other Design Patterns 

Introduction to Design Patterns

Creational Design Patterns

Structural Design Patterns

Behavioral Design Patterns

Java Design Patterns Book

 

9 thoughts on “Singleton Design Pattern in Java with all Scenarios

  1. The best thing is that all the scenarios of a Singleton pattern are at one place with complete explanation & examples. Very helpful to clear all the doubts of Singleton.

  2. Man you are such a gem! After reading this my l doubts ,misconception reageding Singleton seems to be addressed. Keep writing and keep sharing such articles

Leave a Reply


Top