← Back to list

Thread Safe Singleton in Java: All Approaches Explained

Have you ever faced a situation where multiple objects of a class caused unexpected bugs in your application? That’s exactly where the…

Risni Dheerasekara · 2026-04-09 11:38 · 1 claps · 4.0 min read
#design-patterns-in-java #design-patterns #thread-safety #java-thread-safety #singleton-design-pattern
Open on Medium ↗
Wiki topics: SAF · Safety & Alignment

Thread Safe Singleton in Java: All Approaches Explained

Have you ever faced a situation where multiple objects of a class caused unexpected bugs in your application? That’s exactly where the Singleton Design Pattern becomes useful. But basic Singleton is NOT thread safe and in real world applications, that can lead to serious issues.

In this article, we’ll explore:

  • What Singleton is
  • Why thread safety matters
  • All approaches to make Singleton thread safe (with Java examples)
  • Best practices used in the industry

What is Singleton Pattern?

The Singleton Pattern ensures that:

  • Only one instance of a class exists
  • A global access point is provided

Think of it like:

  • A logger (only one needed)
  • A configuration manager
  • A database connection manager

Basic Singleton (NOT Thread-Safe)

Problem: In a multi-threaded environment, two threads can enter getInstance() at the same time:

Result:

  • Multiple objects created
  • Breaks Singleton principle

Why Thread Safety Matters

In real applications:

  • Web servers handle multiple requests (multi threaded)
  • Microservices run concurrent operations

If Singleton is not thread safe:

  • Race conditions occur
  • Data inconsistency happens
  • Hard to debug production issues arise

Approach 1 : Eager Initalization

Here Instance is created during class loading and JVM class loader guarantees thread safety.

Benefit: Thread-safe by default

Drawback: Object created even if not used

Approach 2 : Synchronized Method

Here synchronized ensures no two threads create instance at same time and it prevents race condition.

Drawback: Since all calls to getInstance() is synchronized even after object is created. This is bad as locking is expensive and also it slows down application.

Approach 3 : Double checked locking method (DCL)

Creating a Singleton naively (just if (instance == null) { instance = new Singleton(); }) breaks in multithreaded environments because two threads can both pass the null check before either has finished creating the object. The fix is synchronized, but locking on every call to getInstance() is expensive. DCL is the optimization: lock only when the instance hasn't been created yet.

The operation instance = new Singleton() is actually three steps internally,

  1. Allocate raw memory for the object
  2. Initialize the object
  3. Assign the memory address to the instance reference

Without volatile, a thread could see a non-null instance reference after step 1 but before step 3 which means it gets a pointer to a half-constructed, uninitialized object and tries to use it.

volatile forces the write to be immediately visible to all threads, and prevents this reordering.

The 1st check (instance == null) is for performance where it skips the lock if instance already exists.

The 2nd check (instance == null) acts as a correctness guard. If two threads pass the first check simultaneously, once one thread creates the instance, the second check prevents the other thread from creating another instance.

Benefits: High performance, Thread-safe

Drawback: Complex

Approach 4: Bill Pugh Singleton

Holderclass holds the instance and not loaded when Singleton is loaded. It only gets loaded the first time someone calls getInstance(). At that moment, the JVM initializes Holder, which creates the Singleton instance exactly once. Every call after that just returns the same object.

Why it’s thread-safe:

The JVM specification guarantees that class initialization is atomic. No two threads can initialize the same class simultaneously. So you get thread safety for free without writing a single synchronized or volatile keyword.

Why it’s better than Double-Checked Locking:

Double-checked locking achieves the same result, but it requires volatile and synchronized to be used correctly, which makes it complex. Missing even one keyword can introduce bugs. The Holder pattern does not require such keywords.

Benefits :

  • Lazy loading
  • Thread-safe
  • No synchronization overhead

Approach 5: Enum Singleton

Enum constants are created when the enum class is loaded, and Java ensures that happens exactly once. They are implicitly public static finalwhere created once visible everywhere and never reassignable. You cannot call new Singleton() from anywhere because enums have no accessible constructor.

Benefits:

  • Thread-safe by default
  • Prevents: Reflection attacks , Serialization issues

Drawback : Less flexible

Comparison Table

Use Bill Pugh by default -lazy, fast, thread safe, no extra keywords needed. Use Enum if you need protection against serialization or reflection attacks. Avoid DCL unless you have a specific performance reason -one missing volatile and you have a production bug that's nearly impossible to reproduce. Never use Basic Singleton in any multithreaded environment.

When Should You Use Singleton?

Use when:

  • You need shared resources
  • Only one instance should control behavior

Examples:

  • Logging systems
  • Cache managers
  • Configuration settings

When NOT to Use Singleton

Avoid when:

  • It makes testing difficult
  • Causes tight coupling
  • You need multiple instances later

메타데이터
post_id
ea5b33841fe4
slug
thread-safe-singleton-in-java-all-approaches-explained-ea5b33841fe4
url
https://medium.com/@risni.jeeva/thread-safe-singleton-in-java-all-approaches-explained-ea5b33841fe4
canonical_url
https://medium.com/@risni.jeeva/thread-safe-singleton-in-java-all-approaches-explained-ea5b33841fe4
author_url
https://medium.com/@risni.jeeva
status
ok
fetched_at
2026-06-27 10:07:59