Thread-Safe Singleton Pattern Implementation Strategies in Java

The Singleton pattern restricts a class to a single instance and provides a global point of access to that instance. This pattern is characterized by a private constructor, a private static reference to the single instance, and a public static method (often named getInstance) that returns the instance.

Eager Initialization

In the eager initialization approach, the instance is created at the time of class loading. This method is simple and thread-safe because the class loader mechanism ensures synchronization. However, the instance is created regardless of whether it is used, which may lead to resource waste if the object is never accessed.

package com.design.patterns;

public class EagerSingleton {
    // Instance created immediately when the class is loaded
    private static final EagerSingleton INSTANCE = new EagerSingleton();

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

    // Global access point
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

Lazy Initialization

Lazy initialization delays the creation of the instance until it is actually requested. While this saves resources, a basic implementation is not thread-safe. If multiple threads access the getInstance method simultaneously during initialization, they might create multiple instances.

package com.design.patterns;

public class LazySingleton {
    private static LazySingleton instance = null;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

Thread-Safe Lazy Initialization

To address thread safety in lazy loading, the synchronized keyword can be applied to the method. This ensures that only one thread can execute the initialization logic at a time. The downside is that synchronization is required every time the method is called, introducing a performance overhead.

package com.design.patterns;

public class SyncSingleton {
    private static SyncSingleton instance = null;

    private SyncSingleton() {}

    public static synchronized SyncSingleton getInstance() {
        if (instance == null) {
            instance = new SyncSingleton();
        }
        return instance;
    }
}

Double-Checked Locking

Double-Checked Locking (DCL) optimizes performance by checking the instance existence twice. The first check avoids synchronization once the instance is initialized. The second check, inside the synchronized block, ensures that only one thread creates the instance.

package com.design.patterns;

public class DCLSingleton {
    private static DCLSingleton instance = null;

    private DCLSingleton() {}

    public static DCLSingleton getInstance() {
        // First check (no locking)
        if (instance == null) {
            synchronized (DCLSingleton.class) {
                // Second check (with locking)
                if (instance == null) {
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

Double-Checked Locking with Volatile

The standard DCL implementation can still fail in multi-threaded environments due to instruction reordering. The JVM may reorder the operations inside the constructor and the assignment to the instance variable. Consequently, another thread might see a non-null reference to a partially constructed object.

Using the volatile keyword prevents instruction reordering and establishes a happens-before relationship, ensuring the instance is fully constructed before being visible to other threads.

package com.design.patterns;

public class VolatileSingleton {
    // Volatile ensures visibility and prevents reordering
    private static volatile VolatileSingleton instance = null;

    private VolatileSingleton() {}

    public static VolatileSingleton getInstance() {
        if (instance == null) {
            synchronized (VolatileSingleton.class) {
                if (instance == null) {
                    instance = new VolatileSingleton();
                }
            }
        }
        return instance;
    }
}

Thread Safety Definition

Thread safety refers to the property of a program or segment of code to function correctly during simultaneous execution by multiple threads. Specifically, if multiple threads access shared resources without causing race conditions, data corruption, or inconsistent state, and the outcome remains consistent with a sequential single-threaded execution, the code is considered thread-safe.

Tags: java Design Patterns singleton Concurrency multithreading

Posted on Mon, 07 Sep 2026 16:56:23 +0000 by jasonok6