software-design|October 31, 2019|4 min read

Singleton Pattern with Thread-safe and Reflection-safe

TL;DR

The simplest production-safe Singleton in Java is an enum — it's inherently thread-safe, serialization-safe, and reflection-proof. Double-checked locking works but is more complex for no real benefit.

Singleton Pattern with Thread-safe and Reflection-safe

What is a Singleton Pattern

Following constraints are applied:

  • Where we can restrict the number of instances of a class to just 1
  • No-one should be able to create another instance of the class
  • Should provide a way to get the instance of the method
  • Should be thread safe

Some add-on requirements

  • Should not degrade performance
  • Should also not work if someone instantiate using reflection (Very important actually)

Naive Singleton solution

Some basics around singleton implementation:

  • Need to make constructor as private, so that no-one from outside can call it
  • Need to provide a static method which will return the instance
  • Need to declare a static variable inside class of same type

Lets look at some naive solutions:

class Singleton {
      private static Singleton singleton = new Singleton();

      private Singleton() {}

      public static Singleton getInstance() {
            return singleton;
      }
}

Do you see any issue with above code?


Well, you are instantiating the class on load time itself. If during the lifetime of your code, nobody wants this instance. This instance will be kept in memory unused.

Lazy Initialization

Lets lazy-initialize it

class Singleton {
      private static Singleton singleton = null;

      private Singleton() {}

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

Do you see any issue with above code?


Assume you are working in multi-threaded environment. And, there might be a possibility that more than one thread call getInstance() method at the same time. So, all of them are going to see the instance variable as null. And, each one of them will be calling the constructor, and each will receive a new copy. This is the issue of thread safe.

Solving Thread Safety

class Singleton {
      private static Singleton singleton = null;

      private Singleton() {}

      public synchronized static Singleton getInstance() {
            if (instance == null) {
                  instance = new Singleton();
            }
            return singleton;
      }
}

The most naive thread-safe solution is to make whole getInstance() method as synchronized. Synchronized keyword makes a method thread safe, but with extra overhead. Note: it will take a lock on complete class (static method) unnecessary, which is a costly operation. Whereas, we just need to protect the code where we are checking the instance with null.

Lets optimize it.

Optimized Thread Safe solution

class Singleton {
      private static Singleton singleton = null;

      private Singleton() {}

      public static Singleton getInstance() {
            if (instance == null) {
                  synchronized(Singleton.class) {
                        if (instance == null) {
                              instance = new Singleton();
                        }
                  }
            }
            return singleton;
      }
}

Above approach is called as Double Lock. For the very first time, when it is not instantiated. Threads might come at first check (instance == null), all of them will proceed. But, there comes the lock now. So, only one thread can enter the protected block. And, again we are checking for null. And, instantiating only when it is not initialized. After this thread exists the protected block. Other thread will enter. Now, it will not see this as null, so it will not instantiate it. The benefit we are getting from this is for further calls, we will never take lock after the instance is initialized.


Do you see any issue with above code?


Well, when the program is loaded into memory. OS and JVM performs bunch of optimizations. And, it allocates separate memory to each thread in the stack. There might be a possibility (for very small duration of time) that when one thread instantiates the class, it still remains in its private memory (stack). And, then another thread again instantiates (even after double lock, because it still sees it as null).

There is special keyword in java: volatile, which applies to declaration of variables.

It is an instruction to compiler that *Do not make any optimization to this thing, and make only one copy of this in memory, so that it will always reflect fresh state*

Use of Volatile

Now, take a look at the code.

class Singleton {
      private static volatile Singleton singleton = null;

      private Singleton() {}

      public static Singleton getInstance() {
            if (instance == null) {
                  synchronized(Singleton.class) {
                        if (instance == null) {
                              instance = new Singleton();
                        }
                  }
            }
            return singleton;
      }
}

The singleton class looks OK? Right?

Lets take a look at some advanced optimization. If you have used concept of *reflection* of java, you can still call its constructor. Basically you can access even private things of a class using reflection. So, all constaints goes for a toss?

Lets fix this.

Avoid instantiation via Reflection

class Singleton {
      private static volatile Singleton singleton = null;

      private Singleton() {
            if (instance != null) {
                  throw new RuntimeException("Use only getInstance() to get instance!");
            }
      }

      public static Singleton getInstance() {
            if (instance == null) {
                  synchronized(Singleton.class) {
                        if (instance == null) {
                              instance = new Singleton();
                        }
                  }
            }
            return singleton;
      }
}

Note the constructor definition now.

Pitfalls of Singleton Pattern

Lets look at some pitfalls of this pattern.

  • This pattern is often overused. This pattern is so simple and popular that people tend to use it where it is not required.
  • There are some performance issues associated with singleton pattern. If you wrongly use them.
  • Since there are private constructors and private instance variable. They are difficult to unittest.
  • Its very easy to make it thread-unsafe. If you are not careful, this mistake is very often.
  • People often get confused this with factory pattern.

Related Posts

Principles of Software Designing

Principles of Software Designing

It is very easy to build a software or app. But, it is trickier to have a good…

Deep Dive on Redis: Architecture, Data Structures, and Production Usage

Deep Dive on Redis: Architecture, Data Structures, and Production Usage

“Redis is not just a cache. It’s a data structure server that happens to be…

Deep Dive on Apache Kafka: A System Design Interview Perspective

Deep Dive on Apache Kafka: A System Design Interview Perspective

“Kafka is not a message queue. It’s a distributed commit log that happens to be…

Deep Dive on API Gateway: A System Design Interview Perspective

Deep Dive on API Gateway: A System Design Interview Perspective

“An API Gateway is the front door to your microservices. Every request walks…

Deep Dive on Elasticsearch: A System Design Interview Perspective

Deep Dive on Elasticsearch: A System Design Interview Perspective

“If you’re searching, filtering, or aggregating over large volumes of semi…

REST API Design: Pagination, Versioning, and Best Practices

REST API Design: Pagination, Versioning, and Best Practices

Every time two systems need to talk, someone has to design the contract between…

Latest Posts

Claude Code Skills — Build a Better Engineering Workflow with AI-Powered Code Reviews, Security Scans, and More

Claude Code Skills — Build a Better Engineering Workflow with AI-Powered Code Reviews, Security Scans, and More

Most developers use Claude Code like a search engine — ask a question, get an…

Building an AI Voicebot for Visitor Check-In — A Practical Guide to Handling the Messy Parts

Building an AI Voicebot for Visitor Check-In — A Practical Guide to Handling the Messy Parts

Every office lobby has the same problem: a visitor walks in, nobody’s at the…

Server Security Best Practices — Complete Hardening Guide for Production Systems

Server Security Best Practices — Complete Hardening Guide for Production Systems

Every breach post-mortem tells the same story: an unpatched service, a…

Staff Engineer Study Plan for MAANG Interviews — The Complete 12-Week Roadmap

Staff Engineer Study Plan for MAANG Interviews — The Complete 12-Week Roadmap

If you’re a Senior Engineer (L5) preparing for Staff (L6+) roles at MAANG…

XSS and CSRF Explained — The Complete Guide with Real Attack Examples and Defenses

XSS and CSRF Explained — The Complete Guide with Real Attack Examples and Defenses

XSS and CSRF have been in the OWASP Top 10 for over a decade. They’re among the…

OWASP Top 10 (2021) — Every Vulnerability Explained with Code

OWASP Top 10 (2021) — Every Vulnerability Explained with Code

The OWASP Top 10 is the industry standard for web application security risks. If…