Skip to content

Singleton Pattern — One Instance to Rule Them All

DodaTech Updated 2026-06-24 4 min read

In this tutorial, you'll learn about Singleton Pattern. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

What You'll Learn

You will understand how the Singleton Pattern restricts class instantiation to a single object, why global state management benefits from it, and how to implement it safely in multithreaded environments. By the end, you will be able to recognise appropriate use cases and avoid common pitfalls like hidden dependencies and testability problems.

Why It Matters

Many resources — configuration managers, thread pools, caches, and logging services — should exist exactly once. Without Singleton, you risk duplicate state, inconsistent data, and wasted memory. Mastering Singleton gives you a reliable tool for controlled, predictable resource management. Consider an application configuration: if two parts of the system load separate copies of the config file, one might see updated settings while the other still uses stale values. Singleton ensures that absolutely every consumer reads from the same live object, eliminating this class of inconsistency.

Real-World Use

Database connection pools and application configuration managers rely on Singleton to ensure all parts of an application share the same settings or connection factory. DodaTech's own runtime configuration loader uses a Singleton-backed registry so every module reads from the same source of truth. Another common example is logging frameworks — the Python logging module maintains a single root logger hierarchy accessed via logging.getLogger(), ensuring that log formatting, levels, and handlers are configured once and used everywhere.

The Pattern

Singleton guarantees one instance and a global access point. The constructor is hidden from direct calls, and a static method returns the single instance.

import threading

class ConfigManager:
    _instance = None
    _lock = threading.Lock()

    def __init__(self):
        self.settings = {}

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = cls()
        return cls._instance

    def set(self, key, value):
        self.settings[key] = value

    def get(self, key):
        return self.settings.get(key)
# Usage
c1 = ConfigManager.get_instance()
c2 = ConfigManager.get_instance()
c1.set("theme", "dark")
print(c2.get("theme"))
dark

The double-checked locking pattern in the code above is critical for Thread Safety. First, we check if _instance is None without acquiring the lock — this avoids the overhead of locking on every call after the instance exists. If it is None, we acquire the lock and check again because another thread might have created the instance between the first check and the lock acquisition. This two-step pattern is the standard way to implement a performant, thread-safe Singleton.

Structure

classDiagram
    class Singleton {
        -static instance: Singleton
        -Singleton()
        +static getInstance(): Singleton
    }
    Client --> Singleton : uses

Real-World Usage

  • Java Runtime.getRuntime() — provides a single point of access to the JVM runtime environment.
  • Python's logging module — logger hierarchy managed by a single root logger factory.
  • Node.js module caching — modules are cached after first require, effectively acting as Singletons per process.
  • Spring Framework — beans default to Singleton scope inside the application context.
  • Factory Method often creates Singleton instances behind the scenes.
  • Abstract Factory can return a Singleton factory.
  • Registry is sometimes implemented as a Singleton.

Pros and Cons

Pros Cons
Controlled access to single instance Introduces global state, which can hinder testing
Reduced memory footprint for shared resources Violates Single Responsibility Principle
Lazy initialization possible Hard to subclass in languages with static methods
Thread Safety can be achieved with double-checked locking Can mask poor design choices in tightly coupled systems

Practice Questions

  1. How would you implement a thread-safe Singleton in a language without built-in locking primitives? Consider alternatives like language-level class initialisation guarantees (Java static initialiser, Python module-level import).
  2. When would eager initialization be preferable over lazy initialization in a Singleton? Think about startup time versus runtime performance guarantees.
  3. Can a Singleton class be serialized safely? What precautions are needed to prevent deserialisation from creating a second instance?
  4. How do unit tests become more complex when the system under test relies on a Singleton? What strategies exist to reset Singleton state between tests?

Challenge

Implement a Singleton that supports a "reset" method for testing purposes but throws an exception if called outside of a test environment. Use an environment variable or a flag to control this behaviour safely.

Real-World Task

Examine your current project and identify three services that are instantiated multiple times but should only exist once — a database connection, a configuration loader, or a thread pool. Refactor them to use the Singleton Pattern and use DodaTech's dependency explorer to visualise how many modules depend on your Singleton. If the number is high, consider refactoring toward Dependency Injection.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro