StackWise
Design PatternsCreational

Singleton Pattern

Ensure a class has exactly one instance and provide a global access point to it — and know when not to.

beginner 3 min readLesson 3 of 25

Intent

Singleton guarantees that a class has only one instance and gives the rest of the program a well-known way to reach it. Typical candidates are things that are expensive to build or that must be shared: a configuration registry, a connection pool, a metrics registry.

classDiagram
  class Singleton {
    -Singleton instance$
    -Singleton()
    +getInstance()$ Singleton
    +operation() void
  }
  Singleton --> Singleton : holds
  class Client
  Client ..> Singleton : getInstance()

Implementations compared

public enum AppConfig {
    INSTANCE;

    private final Map<String, String> values = new ConcurrentHashMap<>();

    public String get(String key) { return values.get(key); }
    public void put(String key, String value) { values.put(key, value); }
}
public final class AppConfig {
    private AppConfig() {}

    // The JVM initialises Holder lazily and thread-safely on first access
    private static class Holder {
        static final AppConfig INSTANCE = new AppConfig();
    }

    public static AppConfig getInstance() {
        return Holder.INSTANCE;
    }
}
public final class AppConfig {
    private static volatile AppConfig instance; // volatile is essential

    private AppConfig() {}

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

Try it

SingletonDemo.javaJava 21
import java.util.*;
import java.util.concurrent.*;

enum Registry {
    INSTANCE;
    private final Map<String, Integer> counters = new ConcurrentHashMap<>();
    int increment(String key) { return counters.merge(key, 1, Integer::sum); }
}

class Main {
    public static void main(String[] args) throws Exception {
        try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 1_000; i++) {
                pool.submit(() -> Registry.INSTANCE.increment("hits"));
            }
        }
        System.out.println("Same instance? " + (Registry.INSTANCE == Registry.valueOf("INSTANCE")));
        System.out.println("hits = " + Registry.INSTANCE.increment("hits"));
    }
}

When to use it — and when not

Use it when Avoid it when
Exactly one instance is a true domain rule You just want a convenient global variable
The object is costly and safely shareable It holds mutable state that makes tests order-dependent
You’re not inside a DI container You use Spring — beans are singletons by default
Finished this lesson?

Mark it complete to track your progress through Design Patterns.