Creational

Singleton Pattern

Guarantee one instance, provide global access. A foundational creational pattern โ€” the first pattern every developer learns, and the first one they misuse.

Overview ยท Creational ยท Singleton ยท Factory Method ยท Abstract Factory ยท Builder ยท Prototype ยท Adapter ยท Bridge ยท Composite ยท Decorator ยท Facade ยท Flyweight ยท Proxy ยท Observer ยท Strategy ยท Command ยท Template Method ยท Chain of Resp. ยท State ยท Mediator ยท Iterator ยท Visitor ยท Memento ยท Interpreter
Category: Creational Difficulty: Foundational Interview: Tier 1 Confused with: Prototype
01
Section One ยท The Problem

Why Singleton Exists

Imagine you are building an application logger. Every module โ€” authentication, payments, notifications โ€” needs to write log entries. The naive approach: each module creates its own Logger instance. Now you have three separate loggers, three separate file handles, three separate buffers. Log entries interleave unpredictably, files get corrupted, and there is no single place to change the log level at runtime. Worse, if the logger holds an expensive resource like a database connection pool, you have tripled your memory footprint for no reason.

Naive approach โ€” multiple instances, no coordination
// โœ— Every module creates its own Logger โ€” no coordination class AuthService { private Logger log = new Logger("app.log"); // instance #1 } class PaymentService { private Logger log = new Logger("app.log"); // instance #2 โ€” same file! } class NotificationService { private Logger log = new Logger("app.log"); // instance #3 โ€” same file! }

What goes wrong:

  • Resource duplication โ€” three file handles to the same file; three connection pools to the same database
  • Inconsistent state โ€” one logger sets level to DEBUG, another stays at INFO โ€” behaviour diverges
  • Concurrency corruption โ€” multiple instances writing to the same file without coordination causes garbled output
  • No single control point โ€” changing log format or level requires hunting down every instance across the codebase
Without Singleton โ€” duplicated instances, no coordination
AuthService PaymentService NotificationService Logger #1 Logger #2 Logger #3 โœ— 3 instances โœ— 3 file handles โœ— No coordination

This is the problem Singleton solves โ€” guarantee exactly one instance of a class exists and give every part of the application a single, coordinated way to reach it.

02
Section Two ยท The Pattern

What Is Singleton?

Singleton is a creational pattern that restricts a class to exactly one instance and provides a global access point to it. The class itself controls its own instantiation โ€” the constructor is private, and a static method returns the sole instance, creating it lazily on the first call.

GoF Intent: “Ensure a class only has one instance, and provide a global point of access to it.”
— Gamma, Helm, Johnson, Vlissides (1994)
Analogy — the country’s president: There is exactly one president at any time. You don’t “create” a new president by calling a constructor โ€” there is an official process (an election) that fills the single seat. Every citizen, every ministry, every foreign ambassador references the same president. If someone tries to install a second president, the system rejects it. That’s Singleton โ€” one instance, controlled creation, universal access.

Key insight: The class is responsible for ensuring its own uniqueness. Callers don’t need to check whether an instance exists โ€” they simply call getInstance() and always receive the same object. The control lives in the class, not in the client code.

Constructor is private
No external code can instantiate the class with new
Static field holds the sole instance
One object lives for the application’s lifetime
Static getInstance() method is the only entry point
Every caller receives the same reference โ€” shared state, coordinated access
Lazy initialization creates instance on first call
No cost if the instance is never needed; thread-safety becomes the concern
03
Section Three ยท Anatomy

Participants & Structure

Participant Role In the Analogy
Singleton The class that controls its own instantiation. Stores the single instance in a private static field and exposes it via a static getInstance() method. The constructor is private. The office of the president — exactly one exists, and it controls who occupies it.
Client Any code that needs the shared instance. Calls Singleton.getInstance() instead of new Singleton(). A citizen or ministry — they reference the president’s office, they don’t create a new one.
Singleton — UML Class Diagram
Singleton − instance : Singleton − logLevel : String − logFile : FileWriter − Singleton() + getInstance() : Singleton + log(msg : String) : void «creational» AuthService PaymentService getInstance() getInstance() ✔ same object returned
Only two participants:
  • Singleton is the simplest GoF pattern structurally — one class, one static field, one static method.
  • The complexity lives in the implementation (thread safety, serialization, reflection), not in the number of participants.
04
Section Four ยท How It Works

The Pattern In Motion

Scenario: An application logger used by AuthService and PaymentService.

Step 1 — AuthService calls Logger.getInstance()
The static field instance is null. The private constructor runs — a new Logger is created, stored in the static field, and returned.
Step 2 — AuthService calls logger.log("User logged in")
The logger writes to its file. The instance is alive and holding the file handle.
Step 3 — PaymentService calls Logger.getInstance()
The static field is not null anymore. The constructor does NOT run. The exact same object from Step 1 is returned.
Step 4 — PaymentService calls logger.log("Payment processed")
Same file handle, same buffer, same log level. Both services write to the same coordinated logger — no duplication, no conflict.
Before / After — multiple instances vs. shared Singleton
BEFORE — no pattern Auth Logger #1 Payment Logger #2 Notif. Logger #3 ✗ duplicated ✗ uncoordinated AFTER — Singleton Auth Payment Notif. Logger single instance ✔ one instance ✔ one file handle ✔ coordinated state 1st call → creates instance | 2nd+ calls → returns same object
The pattern in pseudocode
class Logger { private static Logger instance; // the single instance private Logger() { ... } // hidden constructor public static Logger getInstance() { if (instance == null) // first call? instance = new Logger(); // create once return instance; // always same object } }
Thread-safety warning:
  • The pseudocode above is NOT thread-safe.
  • If two threads call getInstance() simultaneously when instance is still null, both pass the null check and create separate instances.
  • Section 6 shows the three safe approaches: synchronized, double-checked locking with volatile, and the enum idiom.
05
Section Five ยท Java Stdlib

You Already Use This

You already use Singleton every time you call Runtime.getRuntime(). The JVM creates exactly one Runtime object โ€” there is no public constructor. Several other JDK classes follow the same pattern.

IN JAVA
Example 1 java.lang.Runtime.getRuntime() — returns the sole Runtime object. You cannot instantiate Runtime directly; the JVM creates exactly one, and getRuntime() returns it. Used for memory stats, GC triggers, and process execution.
Example 2 java.awt.Desktop.getDesktop() — returns the singleton Desktop instance for the current platform. Opening a file, browsing a URL, and composing mail all go through this one object.
Not a Singleton java.util.logging.Logger.getLogger("name") is NOT a Singleton — it returns different instances for different names. That’s a Registry (Multiton) pattern, not Singleton.
Stdlib usage — Runtime singleton
// You never write: new Runtime() โ€” the constructor is package-private Runtime rt = Runtime.getRuntime(); System.out.println(rt.availableProcessors()); // e.g. 8 System.out.println(rt.maxMemory() / 1024 / 1024 + " MB"); // e.g. 4096 MB // Every call returns the same object โ€” guaranteed Runtime rt2 = Runtime.getRuntime(); System.out.println(rt == rt2); // true
Desktop caveat:
  • Desktop.getDesktop() throws UnsupportedOperationException on headless servers.
  • Check Desktop.isDesktopSupported() first.
  • This is a real-world example of why Singletons sometimes need a guard โ€” the “single instance” may not exist at all on certain platforms.
06
Section Six ยท Implementation

Build It Once

Domain: Application Logger. Thread-safe, lazy-initialized, with configurable log level. We show the double-checked locking approach here — the modal includes the enum alternative and a Python equivalent.

Java — Thread-safe Singleton Logger (double-checked locking)
public final class AppLogger { // volatile ensures visibility across threads after construction private static volatile AppLogger instance; private String logLevel; private int messageCount; // private constructor โ€” no external instantiation private AppLogger() { this.logLevel = "INFO"; this.messageCount = 0; System.out.println("[AppLogger] Instance created"); } // double-checked locking: thread-safe, lazy, minimal lock cost public static AppLogger getInstance() { if (instance == null) { // 1st check (no lock) synchronized (AppLogger.class) { if (instance == null) { // 2nd check (locked) instance = new AppLogger(); } } } return instance; } public void log(String msg) { messageCount++; System.out.println("[" + logLevel + "] #" + messageCount + ": " + msg); } public void setLogLevel(String level) { this.logLevel = level; System.out.println("[AppLogger] Level changed to: " + level); } }
Alternative: Enum Singleton (safest in Java)
Java — Enum Singleton (thread-safe, serialization-safe, reflection-safe)
// The JVM guarantees exactly one instance โ€” no DCL, no volatile needed public enum AppLogger { INSTANCE; private String logLevel = "INFO"; private int messageCount = 0; public void log(String msg) { messageCount++; System.out.println("[" + logLevel + "] #" + messageCount + ": " + msg); } public void setLogLevel(String level) { this.logLevel = level; } } // Usage: AppLogger.INSTANCE.log("Started");
Which approach to choose?:
  • Use enum when you need zero-config safety (serialization, reflection, thread-safe).
  • Use DCL + volatile when you need lazy initialization with constructor arguments.
  • Use Bill Pugh holder (static inner class) when you want lazy init without synchronized blocks.
  • Avoid plain lazy init (no sync) — it is broken in multithreaded code.
07
Section Seven ยท Watch Out

Common Mistakes

Mistake #1 — Double-checked locking without volatile: This is the most critical Singleton bug. Without volatile, a thread can see a non-null instance reference before the constructor has finished executing — the JVM is allowed to reorder the write of the reference and the initialisation of the object’s fields. The result: one thread gets a half-constructed object with null fields.
✗ Broken — missing volatile
// โœ— WRONG: Thread B can see instance != null before constructor finishes private static AppLogger instance; // no volatile! public static AppLogger getInstance() { if (instance == null) { synchronized (AppLogger.class) { if (instance == null) instance = new AppLogger(); // instruction reordering risk! } } return instance; // may return partially-constructed object }
✔ Fixed — add volatile
private static volatile AppLogger instance; // โ† volatile prevents reordering
Mistake #2 — Using Singleton as a global variable bag:
  • If your Singleton accumulates unrelated state — config + DB pool + cache + metrics — it’s a God Object, not a design pattern.
  • A Singleton should encapsulate exactly one responsibility.
  • If you find yourself passing the Singleton to every method, you’ve created a Service Locator anti-pattern.
  • Consider dependency injection instead.
Mistake #3 — Forgetting serialization and reflection: In Java, ObjectInputStream.readObject() creates a new instance bypassing your private constructor. So does Constructor.setAccessible(true). Defences:
  • Implement readResolve() to return the existing instance
  • Or use an enum Singleton — the JVM guarantees it is serialization-safe and reflection-safe
Mistake #4 — Tight coupling kills unit tests:
  • Code that calls Logger.getInstance() directly cannot swap in a mock logger for testing.
  • The fix: depend on an interface (ILogger) and inject the implementation.
  • This lets you use Singleton lifecycle management in production while injecting a mock in tests.
✔ Testable Singleton — depend on interface, inject
// Interface โ€” test code can mock this public interface ILogger { void log(String msg); } // Singleton implements the interface public final class AppLogger implements ILogger { // ... getInstance(), private constructor, etc. } // Service depends on interface, not concrete Singleton public class PaymentService { private final ILogger logger; public PaymentService(ILogger logger) { this.logger = logger; } }
08
Section Eight ยท Decision Guide

When To Use Singleton

Use Singleton When
  • You need exactly one instance of a class — logger, configuration holder, connection pool, thread pool manager
  • The instance must be accessible globally — any module can reach it without being handed a reference
  • Creating multiple instances would cause resource conflicts — two writers to the same file, two pools to the same database
  • The instance needs to coordinate shared mutable state — a central event bus, a cache, or a service registry
  • You need lazy initialization — the expensive object should only be created if actually used
Avoid Singleton When
  • You need multiple instances — e.g. different loggers per module → use a Registry / Multiton instead
  • You need testability — code tightly coupled to getInstance() is hard to mock → use dependency injection
  • You are using it as a global variable bag — if the Singleton has 10 unrelated fields, refactor into multiple classes
  • Your framework already handles lifecycle — Spring @Bean defaults to singleton scope — no need to hand-roll it
Comparison Table
Approach Instances Thread Safety Testability Best For
Singleton ← this Exactly 1 You manage it Hard (needs interface) Logger, Config, Registry
Dependency Injection Configurable Framework-managed Easy (swap impl) Modern apps, Spring/Guice
Static Utility Class 0 (all static) Stateless only Hard (no polymorphism) Pure functions (Math, Collections)
Prototype (clone) Many (copies) Per-copy Easy Expensive-to-create templates
Decision Flowchart
Need exactly one shared instance? Yes No Normal class Using DI framework? Yes @Singleton (DI) Spring, Guice, Dagger No Serialization / reflection safe? Yes Enum Singleton No DCL / Holder volatile + synchronized
09
Section Nine ยท Practice

Problems To Solve

Singleton interview questions test thread safety, lazy initialization trade-offs, and your understanding of when not to use the pattern. Interviewers want to see you discuss trade-offs — not just write getInstance().

Difficulty Problem Key Insight
Easy Thread-Safe Logger
Implement a Singleton Logger that is thread-safe and writes timestamped messages to the console.
Use volatile + double-checked locking, or the enum approach. The interviewer checks: do you know why volatile is required? (instruction reordering)
Medium Connection Pool Manager
Design a Singleton that manages a fixed-size database connection pool. getConnection() blocks if all connections are in use.
The Singleton owns a BlockingQueue<Connection>. Key: Singleton ensures one pool; the queue handles concurrency of borrowing/returning connections.
Medium Application Config (reload-safe)
Build a Singleton config holder that reads from a properties file on first access, and can be reloaded at runtime without creating a new instance.
Expose a reload() method that re-reads the file and swaps internal state atomically. Object identity stays the same — all references see the new config. Test: what happens if a thread reads config mid-reload?
Hard Singleton Registry (Multiton)
Extend Singleton to support named instances: Logger.getInstance("auth") returns one Logger, Logger.getInstance("payment") returns a different one, but repeated calls with the same name return the same object.
Use a ConcurrentHashMap<String, Logger> with computeIfAbsent() — it atomically checks and creates. This is the Registry/Multiton pattern: “one per key” instead of “one total.”
Interview Tip:
  • The interviewer isn’t just testing if you can write getInstance().
  • They want you to discuss: (1) Why is volatile needed? (2) What breaks with serialization/reflection? (3) When would you prefer DI over Singleton? (4) What’s the enum approach and why is it the safest?
  • Show that you understand the trade-offs, not just the syntax.