Design Pattern (3), Singleton

Note: this article is published on 09/05/2024.

These will be a series of articles about Design Patterns. We start from MVC Pattern:

Introduction

The discussions in this article will be as following,

  • A: What is Singleton
  • B: Requirements
    • B.1: Basic:
      • Single Instance
      • Global Access
    • B.2: Implicit Requirements:
      • Thread safety
      • Sealed
      • Not Static
    • B.3: Additional Requirement
      • Lazy Loading
        • with Extra protection for thread safety
  • C: Implementation
    • C.1: Regular (eager loading)
    • C.2: Lazy Loading with
      • Thread Safe

A: What is Singleton

The Singleton Design Pattern is a Creational Design Pattern used to ensure that a class has only one instance and provides a global point of access to it. It is commonly used in scenarios where multiple objects need to access a single shared resource, such as

  • configuration settings,
  • access to a shared resource like a file system, or
  • managing a connection to a database.

For a better understanding, please look at the following diagram:

from Singleton Pattern in C# with Example - Dot Net Tutorials

As you can see in the above diagram, different clients (Client A, Client B, and Client C) are trying to get the singleton instance. Once the client gets the singleton instance, they can invoke the methods (Method 1, Method 2, and Method n) using the same instance, like this

Singleton Design Pattern - Java Code Gists

B: Requirements of Singleton

The following requiremnts must be met as a Singleton design pattern.

B.1: Basic Requirements of the Singleton Design Pattern

  • Single Instance: This Design Pattern ensures that only one instance of the Singleton class is created throughout the application.
  • Global Access: Provides a global access point to that instance.

These requiremtnts are the necessary, otherwise it will not be Singleton.

B.2: Implicit Requrements of the Singleton Design Pattern

  • Thread Safety: When used in a multi-threaded application, the singleton needs to be thread-safe to prevent multiple instances from being created.(see sample [ref])
  • Sealed: to prevent singleton class from being inherited and ensure that only one singleton class instance can exist.(see more details: [ref], [ref])
  • Not Static: besides static is a language feature, whereas Singleton is a Design Pattern, they are belong to two different areas. Statoc Class does not support the following features 
    • A singleton class can be extended.
    • A singleton class can support interface implementation and inheritance
    • A singleton class can be instantiated, initialized and ..., disposed
    • A singleton class can be lazy loaded
      • A Static class is loaded automatically by the CLR when the program containing the class is loaded, and cannot be lazy loaded.

B.3: Additional Requirement of the Singleton Design Pattern

  • Lazy Initialization: We can lazily initialize the singleton instance, which means it is created when it is needed for the first time, not when the application starts.
    • Extra Protection for Thread Safery: needed

C: Implementations

We devide the implementations into two parts, 

  • Regular --- meet the basic requirement of Singleton
  • Lazy Loading --- this feature is better for perfomance, but not necessary for singleton itself. Furthermore, additional protection should be considered for Thread Safety handling.

C.1: Regular

The following is an implementation of a standard singleton pattern implementation (eager loading)

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();
    static Singleton()
    {
    }
    private Singleton()
    {
    }
    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

where

  1. Private Parameterless Constructor: to guarantee the class not being instantiated from outside the class; it will only instantiate from within the class.
  2. Sealed Class: to guarantee the class not be inherited in order to prevent from being instantiated from within a nested classes.
  3. Static Field Variable: will holds the single instance of the class once and only once.
  4. Public Static Method or Property: to access the class and return the singleton instance.

Note

A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed only once. It's called automatically before the first instance is created or any static members are referenced. A static constructor is called at most once.

In a class with a static constructor, any static field initializers will be executed in the order they are written in the code, right before the static constructor is called; if no static constructor exists, the static field initializers will run at an implementation-dependent time before the first access to a static field within that class.

C.2: Lazy Loaded Singleton

In the regular singleton, it is acually an eager loading singleton, while creating the singleInstance variable, we are also initializing that variable and making it ready to be used. This is nothing but Eager Loading of Singleton Instance that the CLR take care of object initialization and Thread-Safety in a Multithreaded Environment.

Now we will implement the lazy loading singleton. Lazy loading defers the creation of the Singleton instance until it is needed. This can be beneficial if the initialization of the instance is expensive or if you want to save resources by not creating the instance until it’s necessary.

In this case, we need to write the object initialization, null checking, and furethermore thread-safety code manually, as the CLR will not handle all these things. There could be several ways to do the job:

  1. No Thread Safe Singleton.
  2. Thread-Safety Singleton using lock.
  3. Thread-Safety Singleton using Double-Check Locking.
  4. Thread-Safery Singleton using .NET 4's Lazy<T> type.

No Thread Safe Singleton:

public sealed class Singleton {
    private Singleton() {}
    private static Singleton instance = null;
    public static Singleton Instance {
        get {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

This code is

  • Lazy Loading, and also
  • Private Parameterless Constructor: to prevent being instantiated from out side of the class
  • Sealed: to prevent being instantiated from in side of the class
  • Public Static Method to return a singleton instance hold by a Static Field Variable

However:

  • The code is not thread-safe.
    • Two different threads could both have evaluated the test (if instance == null) and found it to be true, then both create instances, which violates the singleton pattern.  

Thread-Safety Singleton using lock

public sealed class Singleton {
    Singleton() {}
    private static readonly object lock = new object();
    private static Singleton instance = null;
    public static Singleton Instance {
        get {
            lock(lock) {
                if (instance == null) {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
}

In the code, the thread is locked on a shared object and checks whether an instance has been created or not. It takes care of the memory barrier issue and ensures that only one thread will create an instance. The biggest problem with this is performance; performance suffers since a lock is required every time an instance is requested.  

Thread-Safety Singleton using Double-Check Locking

public sealed class Singleton3 {
    Singleton3() {}
    private static readonly object lock = new object();
    private static Singleton instance = null;
    public static Singleton Instance {
        get {
            if (instance == null) {
                lock(lock) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}

The double check-locking will make the performance bettwe because it checks null first, and only once to enter the lock.

Thread-Safery Singleton using .NET 4's Lazy<T> type

public sealed class Singleton
{
    private Singleton()
    {
    }
    private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());
    public static Singleton Instance
    {
        get
        {
            return lazy.Value;
        }
    }
}

The Lazy<T> Generic Class, introduced as part of .NET Framework 4.0, provides built-in support for lazy initialization, i.e., on-demand object initialization, and it is thread safe. This syntax is similar to the eager singleton, and make the implementation eazier.

Summary

Singleton Design Pattern is to ensure that only one instance of a particular class is available at any given point in time for the entire application.

The implementation includes:

  1. Private Parameterless Constructor: to prevent being instantiated from out side of the class
  2. Sealed: to prevent being instantiated from in side of the class
  3. Static Field Variable: will create and hold the single instance of the class once and only once and thread safe..
    1. Eager Loading:  
    2. Lazy loading: using Lazy<T> type
  4. Public Static Method to return a singleton instance hold by a Static Field Variable

 

Reference