TechTree

 Singleton Design Pattern in Java

By Vineet • Nov 06, 2025

Singleton Class in Java — Complete Guide

What is a Singleton Class?

A Singleton class in Java is a design pattern that ensures:

  1. Only one instance of the class exists in the entire JVM.

  2. It provides a global point of access to that instance.

Example use cases:

  • Database connections

  • Logger classes

  • Configuration managers

  • Thread pools

  • Caches


Why Use Singleton?

  • To control resource usage (only one instance instead of multiple).

  • To avoid inconsistent state (since only one object exists).

  • To share data easily between different parts of the application.


Rules for Singleton Class

  1. Constructor must be private (so no one can create an instance directly).

  2. Class must have a static instance variable of itself.

  3. Must provide a public static method that returns the single instance.


Ways to Create Singleton in Java

There are 5 common ways to implement a Singleton pattern in Java:

Eager Initialization (Simple & Fast)

public class SingletonEager {
    private static final SingletonEager instance = new SingletonEager();

    private SingletonEager() {
        // private constructor
    }

    public static SingletonEager getInstance() {
        return instance;
    }
}

Advantages:

  • Simple and thread-safe (because instance created during class loading)

Disadvantages:

  • Instance created even if not used (wastes memory)


Lazy Initialization (Created When Needed)

public class SingletonLazy {
    private static SingletonLazy instance;

    private SingletonLazy() {}

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

Advantages:

  • Instance created only when required

Disadvantages:

  • Not thread-safe (multiple threads can create multiple instances)


Thread-Safe Singleton (Synchronized Method)

public class SingletonThreadSafe {
    private static SingletonThreadSafe instance;

    private SingletonThreadSafe() {}

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

Advantages:

  • Thread-safe

Disadvantages:

  • Synchronized method reduces performance (slow in multi-threaded apps)


Double-Checked Locking (Best Performance)

public class SingletonDoubleCheck {
    private static volatile SingletonDoubleCheck instance;

    private SingletonDoubleCheck() {}

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

Advantages:

  • Thread-safe and high performance

  • Instance created lazily
    Recommended for most production use cases


Bill Pugh Singleton (Best Modern Way)

public class SingletonBillPugh {
    private SingletonBillPugh() {}

    private static class Helper {
        private static final SingletonBillPugh INSTANCE = new SingletonBillPugh();
    }

    public static SingletonBillPugh getInstance() {
        return Helper.INSTANCE;
    }
}

Advantages:

  • Thread-safe

  • Lazy-loaded

  • No synchronization overhead


Most efficient and recommended approach

Enum Singleton (Simplest & Serialization Safe)

public enum SingletonEnum {
    INSTANCE;

    public void doSomething() {
        System.out.println("Doing something...");
    }
}

Advantages:

  • Thread-safe

  • Serialization & reflection safe

  • Simple syntax

Best for single-instance services (like loggers, managers)

Preventing Reflection & Serialization Issues


Reflection can break singleton by invoking private constructor:

Constructor<SingletonEager> constructor = SingletonEager.class.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonEager instance2 = constructor.newInstance();

To prevent this, throw an exception inside the constructor if instance already exists:

private SingletonEager() {
    if (instance != null) {
        throw new RuntimeException("Use getInstance() method instead!");
    }
}

For serialization, implement readResolve():

 
protected Object readResolve() {
    return getInstance()
}

Comments (0)

No comments yet. Be the first to comment!

Leave a Comment