The Singleton Pattern: Know how to manage Resource Effectively

Published by

on

Imagine a large, bustling company where several teams work on critical projects at different locations. Each team needs to send important updates, requests, or urgent messages back to the head office regularly. There is only one communication device available to all teams—a complex and old-fashioned piece of equipment, like a specialized fax machine or satellite phone.

The device isn’t easy to use; it requires specific training, and not everyone can operate it. Each team has their own person trained to handle this device, but since there’s only one machine, multiple teams often find themselves competing to use it. In moments when several teams need to send messages urgently, this can lead to chaos. Everyone scrambles for the device at the same time, delaying communication, creating bottlenecks, and increasing the chances of human error.

Now, let’s consider a better solution. Instead of each team having its own dedicated person, what if there were only one expert who knows how to operate the device efficiently? This communication expert would receive messages from the teams, prioritize them based on urgency, and send them in an organized manner. No more competing for access, no more confusion. The device is used effectively, and everyone’s message is sent on time.

In this setup:

  • The communication device is used optimally, ensuring no downtime.
  • Only one skilled person is needed to manage the device, eliminating the need for multiple experts.
  • Messages are sent quickly and efficiently, as the expert prioritizes them correctly.

This streamlined communication is like the Singleton Pattern in software development.

Singleton in Programming

In programming, the communication device represents a shared resource like a database connection or a network communication channel. It’s a resource that multiple parts of an application need to access, but having multiple instances of it could lead to inefficiencies and conflicts.

The teams are different parts of the application that need to access the resource.

The expert who handles communication for all teams is the Singleton. Instead of each part of the application creating its own instance of the resource (e.g., database connections), the Singleton ensures that only one instance is created and shared. This ensures efficient use of system resources and prevents conflicts or bottlenecks.

Now, let’s explore the technical details.


What is the Singleton Pattern?

The Singleton Pattern in Object-Oriented Programming (OOP) ensures that a class has only one instance throughout the lifecycle of an application. This single instance provides a global access point to a resource, ensuring consistency, efficient resource management, and controlled access.

Why Use the Singleton Pattern?

  • Resource Optimization: Certain system resources (e.g., database connections, logging systems) are expensive to create and manage. The Singleton Pattern ensures only one instance of these resources is created, thereby conserving memory and reducing overhead.
  • Consistency: When multiple parts of an application need to work with the same data or settings (like configuration settings or logging), having a Singleton ensures that all parts of the system are using the same instance.
  • Global Access Point: Singleton provides a single point of access to a shared resource, making it easier to manage and control.

Use Cases for Singleton

The Singleton Pattern is ideal in scenarios where only one instance of a resource is needed for the entire application:

  1. Database Connection Pooling: Instead of creating a new database connection for every transaction, a Singleton can manage a single connection pool, improving efficiency and reducing connection overhead.
  2. Logging System: It ensures that every part of the application logs messages to the same file or system. Without a Singleton, each log might go to different files, leading to inconsistencies.
  3. Caching: When caching frequently used data, a Singleton can provide a central cache store, ensuring that all parts of the application access and update the same cache.
  4. Configuration Settings: In large applications, configuration settings are accessed by multiple modules. A Singleton ensures that the same settings are used across the application, preventing inconsistent behavior.

Optimizing Resources with Singleton

In a multi-threaded or multi-instance application, creating multiple instances of a resource-heavy class (such as a database connection or network socket) can consume unnecessary memory, increase CPU usage, and lead to resource contention.

By restricting the creation of such resources to a single instance, Singleton ensures that:

  • Memory usage is kept under control because only one object is instantiated.
  • Network connections or database handles are reused efficiently.
  • CPU usage is reduced as there is no need to repeatedly instantiate and manage objects.

Impact of Not Using Singleton Where Needed

If Singleton is not used where appropriate, the following issues may arise:

  1. Memory Bloat: Without a Singleton, every request or operation might create a new instance of the resource, leading to high memory consumption.
  2. Inconsistent Behavior: If multiple instances of a configuration or logging class are created, different parts of the application may end up working with different data, causing inconsistencies.
  3. Performance Bottlenecks: Excessive creation of database connections or network sockets may lead to performance degradation and even crashes in high-load environments.
  4. Thread Contention: Without a single, managed instance, threads may fight over resources, leading to deadlocks or race conditions.

How to Implement Singleton in Programming

Singleton in Core Java

Example: Lazy Initialization with Thread Safety
public class Singleton {
    // Private static variable to hold the single instance
    private static Singleton instance;

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    // Public method to provide access to the instance with thread safety
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public void showMessage() {
        System.out.println("Hello from Singleton!");
    }
}

public class Main {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.showMessage();
    }
}

In this example:

  • Lazy Initialization: The instance is created only when it’s first requested.
  • Thread Safety: synchronized ensures that in multi-threaded environments, only one thread can access the getInstance method at a time.

Singleton in C# (.NET)

Example: Thread-Safe Singleton
public sealed class Singleton {
    private static Singleton instance = null;
    private static readonly object lockObj = new object();

    // Private constructor ensures the class cannot be instantiated from outside
    private Singleton() {}

    public static Singleton Instance {
        get {
            lock (lockObj) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
            return instance;
        }
    }

    public void ShowMessage() {
        Console.WriteLine("Hello from Singleton!");
    }
}

class Program {
    static void Main(string[] args) {
        Singleton singleton = Singleton.Instance;
        singleton.ShowMessage();
    }
}

  • Thread-Safe: The lock ensures thread-safe access to the instance in a multi-threaded environment.

Singleton in Spring Boot

In Spring, Singleton is the default scope for beans. Spring ensures that only one instance of a bean is created, so you don’t need to manually implement the Singleton Pattern.

Example: Spring Boot Singleton Bean

import org.springframework.stereotype.Component;

@Component
public class SingletonService {
    public void showMessage() {
        System.out.println("Hello from Spring Singleton!");
    }
}

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SingletonExampleApplication implements CommandLineRunner {

    private final SingletonService singletonService;

    public SingletonExampleApplication(SingletonService singletonService) {
        this.singletonService = singletonService;
    }

    public static void main(String[] args) {
        SpringApplication.run(SingletonExampleApplication.class, args);
    }

    @Override
    public void run(String... args) {
        singletonService.showMessage();
    }
}

Here, Spring Boot automatically manages the singleton lifecycle, so you don’t need to handle the instance creation.


Singleton in JavaScript/Node.js

In Node.js, modules are cached after the first time they are loaded. This caching behavior effectively creates a Singleton when a module is required multiple times.

Example: Singleton in Node.js

// singleton.js
let instance = null;

class Singleton {
    constructor() {
        if (!instance) {
            instance = this;
        }
        this.message = "Hello from Singleton!";
        return instance;
    }

    showMessage() {
        console.log(this.message);
    }
}

module.exports = Singleton;

// main.js
const Singleton = require('./singleton');

const singleton1 = new Singleton();
const singleton2 = new Singleton();

singleton1.showMessage(); // Output: Hello from Singleton!
singleton2.showMessage(); // Output: Hello from Singleton!

console.log(singleton1 === singleton2); // Output: true

In this example, Node.js ensures the Singleton class behaves as a singleton because of module caching.


Conclusion

The Singleton Pattern is a powerful design pattern in software engineering that ensures efficient resource management by limiting the instantiation of a class to one instance. Whether managing database connections, logging systems, or network communication, Singleton optimizes performance, conserves memory, and ensures consistency throughout the application. Different languages like Java, C#, Spring Boot, and JavaScript (Node.js) implement Singleton in slightly varied ways, but the core principle remains


Disclaimer: This blog contains the individual opinions and perspectives of Vijay Pandurangan, which are not necessarily indicative of the views of his employer. The author assumes no responsibility for any actions taken or decisions made based on the information presented in this blog. Should any content from this blog be referenced or used in articles, white papers, wikis, blogs, or similar formats, it should be attributed solely to Vijay Pandurangan, independent of his professional affiliations. Please note that the use of his employer’s name in such contexts is not permitted.


Discover more from Vijay Pandurangan

Subscribe now to keep reading and get access to the full archive.

Continue reading