Singleton Design Pattern: Eager And Lazy Initialization With Code Example

This is the second part of my sharing of Singleton Design Pattern. In this article, I will explain what is Eager Initialization and Lazy Initialization in Singleton Design Pattern.

You can read the first part of my Singleton Design Pattern in this article: Singleton Design Pattern (Part 1): A Before-and-After Code Analysis.

An Eager Singleton creates an instance of the Singleton class as soon as the application starts, whereas a Lazy Singleton creates the instance only when it’s actually needed.

With the definitions out of the way, let’s dive deeper and understand the differences between these two strategies through some hands-on coding.

public sealed class EagerSingletonLogger
{

    private static readonly EagerSingletonLogger instance = new EagerSingletonLogger();
    private readonly static string path = "D:\\Log\\MyFile.txt";
    private static readonly object fileLock = new object();

    private EagerSingletonLogger()
    {
        Thread.Sleep(2000);
        Console.WriteLine("Eager Instance Created");

    }

    public static EagerSingletonLogger Instance
    {
        get
        {
            return instance;
        }
    }

    public static string DummyEagerProperty
    {
        get
        {
            return "This is a Dummy Eager Property";
        }
    }
    public static void LogMessage(string message)
    {   Console.WriteLine("proceed to LogMessage method of LazySingletonLogger");
        lock (fileLock)
        {
//omitted codes

I’ve introduced another class called EagerSingletonLogger. As you can discern from my previous article, I’ve omitted the GetInstance() method to instantiate the object and have replaced it with:

private static readonly EagerSingletonLogger instance = new EagerSingletonLogger();

With eager initialization, we create an instance of the class as soon as the class is instantiated. I’ve also introduced a property named “DummyEagerProperty” which I’ll use for demonstration later. And with that, we have our eager singleton!

For the lazy singleton, the code is pretty much the same. We use Lazy<T> to create the instance. Here’s the code:

public sealed class LazySingletonLogger
{
    private static readonly Lazy<LazySingletonLogger> lazyInstance = new Lazy<LazySingletonLogger>(() => new LazySingletonLogger());
    private readonly static string path = "D:\\Log\\MyFile.txt";
    private static readonly object fileLock = new object();

    public static LazySingletonLogger Instance { get { return lazyInstance.Value; } }

    private LazySingletonLogger() {
        Thread.Sleep(2000);
        Console.WriteLine("Lazy Instance Created");
    }
    public static string DummyLazyProperty
    { //omitted codes

Now, let’s delve into the main class. I will perform tasks in the following order. Please pay close attention to this sequence, as it’s crucial to highlight the differences between eager and lazy designs:

  1. First, I will invoke the method of the class
  2. Then I will access the property of the class
  3. Finally, I will instantiate the class

Here is the code of my main class:

static void Main(string[] args)
{
    Console.WriteLine("Eager demo starts here");
    EagerSingletonLogger.LogMessage("some text...");//First, access method of eager singleton
    Console.WriteLine(EagerSingletonLogger.DummyEagerProperty);//Second, access property of eager singleton
    EagerSingletonLogger eagerInstance = EagerSingletonLogger.Instance;//Finally, create eager singleton instance
    Console.WriteLine("Eager demo ends here");
    Console.WriteLine("Lazy demo starts here");
    LazySingletonLogger.LogMessage("some text...");//First, access method of lazy singleton
    Console.WriteLine(LazySingletonLogger.DummyLazyProperty);//Second, access property of lazy singleton
    LazySingletonLogger lazyInstance = LazySingletonLogger.Instance;//Third, create lazy singleton instance
    Console.WriteLine("Lazy demo ends here");
    Console.ReadLine();     
}

And this is the result:

Eager Vs Lazy

From the results, it’s evident that the eager singleton always creates the instance first, even though, in my main class, the line EagerSingletonLogger eagerInstance = EagerSingletonLogger.Instance; is placed after invoking the method and accessing the variable.

In contrast, the lazy instance only gets created when the line LazySingletonLogger lazyInstance = LazySingletonLogger.Instance; is executed.

You might wonder: why introduce the complexity of choosing between eager and lazy initialization? If we eventually need the instance, why not always opt for eager initialization? What significant difference does it make if we instantiate it on line 100 or line 500?

The primary reason we don’t always use eager initialization is its potential to be resource-intensive. If the Singleton class undergoes extensive initialization, it might consume significant computing power, storage, network bandwidth, or graphic processing.

Eager initialization doesn’t offer the same degree of control as lazy initialization. For instance, you might wish to initialize your Singleton based on specific conditions, after certain tasks are completed, or perhaps after fetching configurations from a server. Eager initialization restricts such flexibility.

Okay then, it seems like eager singleton have few drawback, why don’t we move to lazy and use lazy forever?

One significant drawback of the lazy singleton design pattern lies in its multi-threaded complexity. The synchronization required can present challenges that might make you question your career choice as a programmer. Another substantial concern with the lazy singleton design pattern is the unpredictable instantiation time. Since the singleton is created upon its first access, if its creation is time-consuming, it might introduce unexpected delays in your application.

So, I think it’s safe to say we need both of them , both of them are completing each other in our life as a programmer, we need to analyze the scenario before decide which strategy need to be implemented.

Eager Vs Lazy

Here’s a simple rule of thumb to navigate this dilemma: Use lazy initialization when the instance is resource-intensive to create and may not always be needed during the application’s runtime. In all other cases, opt for the eager singleton design pattern.

You may have the source code from my Github.