Yellow umbrella among black umbrellas

Design Patterns in Java: Singleton Pattern

Today we’ll continue our journey in design patterns by uncovering the singleton design pattern.

This post is part of the Design Patterns in Java Series. Check out the full list of covered patterns there.

This creational pattern is the simplest from a design perspective as it involves a single class.

However, it has quite a few quirks that might trip us up.

Don’t worry, I’m here to help you. Let’s dive right in.

Why Should You Use the Singleton Design Pattern?

Here are a few ways you can use this pattern:

  • create a logger class for your app – this is the go-to example.
  • create a configuration class – this is used to access configuration data or other global data.
  • load expensive resources only once, thus improving app performance.
  • factory pattern as a singleton – this is used to manage object creation.

What is a Singleton?

Here’s the official definition from the Gang of Four book:

The singleton pattern ensures a class has only one instance, and provides a global point of access to it.

In other words, this pattern allows you to instantiate a class a single time.

Therefore, when you ask for an instance of that class, you always get the same instance.

It has two purposes:

  1. Restricts instantiation of a class to at most one instance.

  2. Provides a global access point to that instance.

The class diagram below shows the gist of the singleton pattern. This is simply a class that holds a reference to the single instance of itself. Access is controlled via the getInstance() method.

Singleton Pattern Class Diagram

Singleton Pattern Class Diagram

Pros

  • global point of access – access shared data through one method.
  • increase performance – load expensive resources once and access them via the singleton pattern.
  • proper management of pools of resources – manage thread pools or connection pools through a single point, otherwise unexpected errors can occur.

Cons

  • increased memory consumption – this is due to needlessly caching resources within our applications.
  • performance issues – high usage of singletons is considered bad OOP design, as we’re most likely not employing advantages of other OOP principles such as inheritance.
  • hard to test – this is due to having to create global states of the app to run a unit test. It’s hard to get repeatable results as these states can vary between runs.

How Can You Implement the Singleton Pattern?

In the following section, I’ll outline the classic ways of implementing the singleton pattern. Note that these are not the best examples due to the problems I’ll soon describe.

If you just want me to give you teh codez, please use the code from the Best Way to Implement the Singleton Pattern section of this article.

If you want to have a better understanding of this pattern, which I hope is the case, read on.

Understanding the Singleton Pattern via Java Code Examples

1. Quick-and-Dirty Implementation with Lazy Instantiation

First of all, observe that the constructor is marked as private. This means that from the outside, you can’t construct objects of this class.

Secondly, the singleton instantiates itself via the getInstance() static method. As this is a static method, it can be accessed from anywhere in your code.

Inside this method, we check if an instance of the singleton already exists. If that’s not the case, then we create it.

Subsequent calls to the getInstance() method return the same, previously created Singleton instance.

Furthermore, I’d like to point out that this is an example of lazy instantiation, as we’re delaying creating the object until we need it, i.e. when the getInstance() method is called the first time.

Concurrency Issues

The code looks all right, but what happens if we have multiple threads calling the getInstance() simultaneously?

This would lead to a race condition, as the getInstance() method is not atomic. Multiple instances of the class could be created by separate threads.

The issue can be addressed by adding the synchronized keyword to the getInstance() method. This puts a lock on the method, preventing two threads from accessing the method at the same time.

2. Synchronised Singleton

The drawback of this method is that synchronisation is an expensive mechanism. If you call the getInstance() method often, this can lead to performance issues.

In fact, we only need synchronisation when creating the singleton instance the first time. After we create the instance, further calls to getInstance() don’t need synchronisation.

3. Eager Instantiation Implementation

With this approach, the JVM creates a unique instance of the singleton when the class is loaded.

This allows us to skip synchronising the getInstance() method, as the singleton object will already be created before any class accesses the instance variable.

4. Double-Checked Locking Implementation

With this mechanism, we first check if an instance of the singleton exists, and if not, we put a lock on the class and check again before creating the object.

The obvious advantage of this approach is that we’re using synchronisation only when we’re initialising the Singleton instance. This is more efficient than the Synchronised Singleton method previously mentioned.

It’s also important to point out that the volatile keyword above ensures the initialisation of the instance variable is visible to other threads immediately, instead of a cached reference.

Best Way to Implement the Singleton Pattern in Java

I hate to break it to you, but all of the above methods aren’t bulletproof.

The main issue is that the Java Reflection API can be used to change the access modifier of the constructor of the Singleton class from private to public.

This of course would allow creation of more than one instance of the singleton, which isn’t something we want.

The recommended way to create singletons in Java is by using the ENUM type. This approach is proposed by Joshua Bloch in his Effective Java book.

5. Enum Type Implementation

Using enum types, we get rid of synchronisation problems and also avoid the reflection issue.

Therefore, this approach guarantees that only one instance of the singleton class is created.

Summary

In conclusion, my four key points are:

  1. The singleton pattern makes sure that a single instance of a class is created.

  2. The singleton is used to control global access to shared data and for caching expensive resources.

  3. Due to its apparent simplicity, the singleton pattern can be overused, thus introducing maintainability and performance issues.

  4. Be aware of race conditions in multithreaded environments – use the ENUM class to guarantee thread-safety.

This is a tricky pattern so I really hope this post helped improve your understanding of it.

About the Author Dragos Stanciu

follow me on:

Subscribe

Like this article? Stay updated by subscribing to my weekly newsletter:

Leave a Comment: