You are here
Home > java >

Singleton Design Pattern in Java with all Scenarios

Singleton Design Pattern in JavaIn 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.

There are many situations where having exactly one instance of a class is crucial. If multiple instances were allowed, it could lead to incorrect program behavior, inefficient resource utilization, or inconsistent results. Here are some common use cases for the Singleton Design Pattern in Java:

  • Logging: A single logging instance can manage log files, ensuring that all log messages are written to the same file without conflicts.
  • Configuration Management: A configuration manager can load application settings once and provide consistent access to these settings across the application.
  • Thread Pools: Managing a single pool of threads ensures efficient resource allocation and prevents the overhead of creating new threads repeatedly.
  • Caching: A single cache instance can store frequently accessed data, improving application performance by reducing redundant data retrieval operations.
  • Device Drivers: In systems interacting with hardware, a single driver instance can prevent conflicts and ensure proper control over the device. Even within the Java Development Kit (JDK), you can find examples of the Singleton pattern. For instance, java.lang.Runtime and java.awt.Desktop are classic examples where only one instance is needed to manage system-wide resources or desktop interactions

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.

Best Practices for Implementing Singleton

While the Enum Singleton is generally the most recommended approach, here are some best practices to consider when implementing the Singleton pattern in Java:

  • Prefer Enum Singleton: For most new development, the Enum Singleton is the simplest, most robust, and safest way to implement the pattern, as it inherently handles thread safety, serialization, and reflection issues.
  • Consider Bill Pugh Singleton for Lazy Initialization: If you cannot use an Enum (e.g., due to legacy code or specific framework constraints) and require lazy initialization, the Bill Pugh Singleton (Initialization-on-demand holder idiom) is an excellent alternative that provides thread safety without explicit synchronization overhead.
  • Avoid Public Constructors: Always keep the Singleton constructor private to prevent external instantiation.
  • Guard Against Violations: If not using Enum Singleton, implement safeguards against reflection, cloning, and serialization/deserialization to maintain the Singleton integrity.
  • Keep it Simple: Do not over-engineer your Singleton. Choose the simplest implementation that meets your requirements for thread safety and lazy loading.
  • Test Thoroughly: Always test your Singleton implementation in a multi-threaded environment to ensure it behaves as expected and truly maintains a single instance.

FAQs

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

Practice Set for all 23 GoF’s Design Patterns 

GoF’s Design Patterns Practice Test

Download Java Design Patterns Solved Interview Questions PDF

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