Java
Double-Checked Locking
Multithreading
Concurrency
Programming Fix

How to solve the Double-Checked Locking is Broken Declaration in Java?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

The phrase "double-checked locking is broken" refers to the old unsafe form of lazy singleton initialization in Java. The short modern answer is: double-checked locking is safe in Java if the instance field is declared volatile, but many codebases should still prefer simpler patterns such as the initialization-on-demand holder idiom or an enum singleton.

Why the Old Version Was Broken

The classic broken pattern looked like this:

java
1public class BrokenSingleton {
2    private static BrokenSingleton instance;
3
4    public static BrokenSingleton getInstance() {
5        if (instance == null) {
6            synchronized (BrokenSingleton.class) {
7                if (instance == null) {
8                    instance = new BrokenSingleton();
9                }
10            }
11        }
12        return instance;
13    }
14}

The problem is not the two if checks themselves. The problem is publication without the right memory-visibility guarantees. Without volatile, another thread can observe a reference to an incompletely constructed object due to reordering and visibility effects.

The Safe Modern Version

In modern Java, the standard double-checked locking fix is to make the field volatile.

java
1public class SafeSingleton {
2    private static volatile SafeSingleton instance;
3
4    private SafeSingleton() {
5    }
6
7    public static SafeSingleton getInstance() {
8        if (instance == null) {
9            synchronized (SafeSingleton.class) {
10                if (instance == null) {
11                    instance = new SafeSingleton();
12                }
13            }
14        }
15        return instance;
16    }
17}

volatile ensures the write to instance is published safely and that readers do not observe a partially initialized object.

What volatile Actually Fixes

The key guarantee is not merely "always read from memory". The important part is safe publication and the required happens-before relationship around writes and reads of the singleton reference.

That makes the constructed object visible only after construction is complete from the perspective required by the Java memory model.

When to Prefer the Holder Idiom

Even though volatile makes double-checked locking correct, the holder pattern is often simpler and clearer.

java
1public class HolderSingleton {
2    private HolderSingleton() {
3    }
4
5    private static class Holder {
6        private static final HolderSingleton INSTANCE = new HolderSingleton();
7    }
8
9    public static HolderSingleton getInstance() {
10        return Holder.INSTANCE;
11    }
12}

This gives you lazy initialization without explicit synchronization code in getInstance.

Enum Singleton Is Even Simpler

If you truly need one singleton instance and no lazy holder complexity, an enum is often the cleanest option.

java
1public enum ConfigRegistry {
2    INSTANCE;
3
4    public String getValue() {
5        return "ready";
6    }
7}

Usage:

java
System.out.println(ConfigRegistry.INSTANCE.getValue());

This is concise, thread-safe, and robust against several serialization pitfalls that affect ordinary singleton classes.

Which Approach Should You Choose

A practical rule set:

  • use volatile double-checked locking only if you specifically need that shape
  • use the holder idiom for clear lazy initialization
  • use an enum when you want the simplest singleton form

Many teams reach for double-checked locking by habit when one of the other patterns would be easier to read and maintain.

Performance Considerations

Double-checked locking exists to avoid paying synchronization cost on every call after initialization. In modern JVMs, that overhead is often far less important than people assume, especially compared with correctness and clarity. Do not introduce concurrency complexity based only on old folklore.

Measure if singleton-access performance is truly a hotspot.

Common Pitfalls

The most common mistake is using double-checked locking without volatile and assuming the synchronized block alone is enough. Another is treating "double-checked locking is broken" as a timeless statement without understanding that modern Java memory rules make the volatile form safe. Teams also sometimes use a complicated singleton pattern when a holder class or enum would be clearer. Finally, over-optimizing singleton creation paths without evidence often adds complexity for no real gain.

Summary

  • The old form of double-checked locking in Java was broken because of unsafe publication.
  • The modern safe form requires the singleton field to be volatile.
  • The holder idiom is often a cleaner lazy-initialization alternative.
  • Enum singletons are simple and robust when they fit the design.
  • Prefer the simplest correct pattern unless profiling justifies the more complex one.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.