Singleton Class in Java Design Pattern – Best Practices with Examples (original) (raw)

In this post, we feature a comprehensive Tutorial on Singleton Class in Java. Design Patterns in Java are incredibly popular among the software developers. One of the most common interview questions is the Singleton Design Pattern. So in this tutorial, I’ll summarize the best practices which will help developers dodge general issues and develop better applications.

1. What are Design Patterns?

A design pattern is known as a well-proven solution to a commonly occurring problem in the software design. They can speed up the development process by providing the tested and proven development paradigms. Using design patterns offer few advantages such as:

Java has several design patterns and the Singleton Pattern is the most commonly used.

1.1 What is Java Singleton Design Pattern?

Singleton Class in Java - Classic Singleton Design Pattern

Fig. 1: Classic Singleton Design Pattern

1.2 Pre-requisites

To implement the this design pattern in the Java programming language, developers need to have the following:

1.3 Structure

In the Java programming language, there are different implementations of the singleton pattern. But before we start, the singleton design pattern should be considered only if all the three criteria’s are satisfied i.e.

Over here is the classic implementation of the Singleton Design Pattern.

SingletonClassDemo.java

01020304050607080910111213141516171819202122 package com.java.design.pattern.singleton;public class SingletonClassDemo { private static SingletonClassDemo instance = null; private SingletonClassDemo() { } public static SingletonClassDemo getInstance() { if (instance == null) { instance = new SingletonClassDemo(); } return instance; }}

In the preceding example, we wrote a class with a method that creates a new instance of the class if one does not exist. Do note:

This example is known as Lazy Initialization – which means that it restricts the instance creation until it is requested for the first time.

1.4 Real-time examples

Here represent some significant scenarios where the singleton design pattern is used.

Note: If a Singleton class is loaded by two classloaders, two instances of the Singleton class will be created (i.e. one for each classloader).

In this section, we will explore the strategies that can be adopted to improve the Singleton design pattern.

2.1 Eager Initialization

In eager initialization, the instance of the singleton class is created at the time of class loading. This approach offers the easiest execution and helps improve the runtime performance of the application. Here is an example of Eager Initialization singleton class.

SingletonClassDemo2.java

01020304050607080910111213141516171819 package com.java.design.pattern.singleton;public class SingletonClassDemo2 { private static final SingletonClassDemo2 instance = new SingletonClassDemo2(); private SingletonClassDemo2() { } public static SingletonClassDemo2 getInstance() { return instance; }}

This approach is similar to lazy initialization, but it has a drawback i.e. the instance is always created even though the application is not utilizing it. This is considered a destructive practice for creating the database connections or sockets as it may lead to memory leak problems.

2.1.1 Static Initialization

The implementation of static block initialization is similar to the eager initialization, except that instance of the class is created in a static block that provides an option for the exception handling.

12345678 static { try { instance = new StaticSingletonClassDemo(); } catch(Exception ex) { throw new RuntimeException("Exception occurred in creating the singleton instance ...!"); }}

2.2 Bill Pugh Singleton

Prior to Java 5, Java memory model had many issues and the developers had to use the Bill Pugh Solution for implementing the Single design pattern in their applications. This approach is based on initialization on demand idiom and uses the Inner classes’ concept. Here is an example of Bill Pugh Solution singleton class.

SingletonBillPughDemo.java

0102030405060708091011121314151617181920 package com.java.design.pattern.singleton;public class SingletonBillPughDemo { private SingletonBillPughDemo() { } private static class Lazyholder { private static final SingletonBillPughDemo INSTANCE = new SingletonBillPughDemo(); } public static SingletonBillPughDemo getInstance() { return Lazyholder.INSTANCE; }}

To a degree, Bill Pugh Singleton has been the good approach, but it is easily destroyed through the Java Reflection API. Hence, this approached was not heartily recommended by the Java developers.

2.3 Using Enum

Enum was introduced in Java 5 and provides a thread-safe implementation. The objects returned by Enum are Singleton in nature and therefore can be effectively used for implementing the Singleton design pattern in the multi-threaded environment.

SingletonEnum.java

1234567 package com.java.design.pattern.singleton;public enum SingletonEnum { INSTANCE;}

This approach is easy but it has 2 drawbacks i.e.

2.4 Thread-Safe Singleton

Consider a scenario if two threads try to create an instance of a singleton class at the same time. In a multi-threaded environment, there is a possibility that separate objects get created, due to different times of accessing the (instance == null) check. This will break the singleton principle. The simplest way of achieving the thread safety in the singleton design pattern is to make the getInstance() method synchronized.

Here is an example of the Thread-Safe singleton class.

SingletonClassDemo3.java

0102030405060708091011121314151617181920212223 package com.java.design.pattern.singleton;public class SingletonClassDemo3 { private static SingletonClassDemo3 instance = null; private SingletonClassDemo3() { } public static synchronized SingletonClassDemo3 getInstance() { if (instance == null) { instance = new SingletonClassDemo3(); } return instance; }}

At this location using the synchronized keyword will ensure thread-safety but the application performance will be degraded. So at one side, we are resolving the problem on another side we are creating one more. To solve this, Double Check Lock principle is used.

2.5 Double Check Locking Principle

The locking mechanism in the singleton design pattern causes the thread to get a lock on the getInstance method only when the instance is null. This prevents the unnecessary synchronization once the instance variable is initialized. Here is an example of a Double Check Locking singleton class.

SingletonClassDemo4.java

01020304050607080910111213141516171819202122232425262728293031 package com.java.design.pattern.singleton;public class SingletonClassDemo4 { private static SingletonClassDemo4 instance = null; private SingletonClassDemo4() { } public static SingletonClassDemo4 getInstance() { if (instance == null) { synchronized(SingletonClassDemo4.class) { if (instance == null) { instance = new SingletonClassDemo4(); } } } return instance; }}

2.6 Using Volatile Keyword

At this point, the singleton implementation looks perfect. But it will still be incomplete without the use of the volatile keyword. This keyword guarantees the happens-before relationship i.e. all the write will happen in the volatile instance before any read of the instance.

SingletonClassDemo5.java

01020304050607080910111213141516171819202122232425262728293031 package com.java.design.pattern.singleton;public class SingletonClassDemo5 { private static volatile SingletonClassDemo5 instance = null; private SingletonClassDemo5() { } public static SingletonClassDemo5 getInstance() { if (instance == null) { synchronized(SingletonClassDemo5.class) { if (instance == null) { instance = new SingletonClassDemo5(); } } } return instance; }}

Up till now, these have been the most widely used approaches for the Singleton design pattern. I am using 5th and the 6th approach in many of my projects as it is easy to understand and implement in the multi-threaded environment.

3. Ways to Kill Singleton

In this section, we will discuss three essential concepts which can break the singleton property of a class and how to prevent them. Let’s discuss them one by one.

3.1 Reflection

Reflection can easily destroy the Singleton design of a class by calling the private constructor and setting the access level to true. Let’s understand this with the help of a code snippet:

010203040506070809101112 try { Constructor[] constructors = Singleton.class.getDeclaredConstructors(); for (Constructor constructor : constructors) { constructor.setAccessible(true); instanceTwo = (Singleton) constructor.newInstance(); break; }} catch (Exception ex) { ex.printStackTrace();}

To overcome this, Enum is used because JVM ensures that the Enum value is instantiated only once and the objects returned by Enum are Singleton in nature. The purpose of using Enum is that its default constructor is private in nature and developers cannot invoke them through the program. Its only drawback is it does not allow the Lazy initialization for the Singleton design pattern.

3.2 Serialization

In distributed systems, the Singleton design can be destroyed during the deserialization process as it’ll create a new instance of the Singleton class. To overcome this issue, developers have to implement the readResolve() method in the Singleton class implementing the Serializable interface. Let’s understand this with the help of a code snippet:

1234 protected Object readResolve() { return getInstance();}

3.3 Cloning

Cloning is a concept where one can produce the copy of an instance and therefore destroying the Singleton design of a class. To overcome this issue, developers have to override the clone() method and throw the CloneNotSupportedException exception from that method. Let’s understand this with the help of a code snippet:

1234 @Overrideprotected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException();}

4. Conclusion

These tips and examples on the Singleton Design Pattern are based on my experience and how I use this design pattern in the Java programming language.

4.1 Thumb Rules

That’s all for this tutorial and I hope the article served you whatever you were looking for. Happy Learning and don’t forget to share!

5. Download the Eclipse Project

This was an example of following the best practices in the Java Singleton Design Pattern.

Photo of Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).