designpatterns/creational booknotes

Singleton pattern ensure that a class has only one instance, while providing a global access point to this instance.

Structure

Drawbacks of the Singleton Design Pattern | Baeldung

Applicability

Use the Singleton pattern when a class in your program should have just a single instance available to all clients; for example, a single data- base object shared by different parts of the program.

Examples

Basic Singleton Implementation (Lazy Initialization, Thread-Unsafe)

class DatabaseManager {
    private static DatabaseManager instance;
 
    // Private constructor prevents instantiation from other classes
    private DatabaseManager() {
        System.out.println("DatabaseManager initialized.");
    }
 
    // Static method to get the single instance
    public static DatabaseManager getInstance() {
        if (instance == null) {
            instance = new DatabaseManager();
        }
        return instance;
    }
 
    public void connect() {
        System.out.println("Connected to the database.");
    }
}
 
// Client Code
public class SingletonExample {
    public static void main(String[] args) {
        DatabaseManager db1 = DatabaseManager.getInstance();
        db1.connect();
 
        DatabaseManager db2 = DatabaseManager.getInstance();
        db2.connect();
 
        System.out.println(db1 == db2); // Output: true (Both are the same instance)
    }
}

Thread-Safe Singleton (Synchronized Method)

class DatabaseManager {
    private static DatabaseManager instance;
 
    private DatabaseManager() {
        System.out.println("DatabaseManager initialized.");
    }
 
    public static synchronized DatabaseManager getInstance() { // synchronized ensures thread safety
        if (instance == null) {
            instance = new DatabaseManager();
        }
        return instance;
    }
}

Bill Pugh Singleton (Lazy + Efficient)

class DatabaseManager {
    private DatabaseManager() {
        System.out.println("DatabaseManager initialized.");
    }
 
    private static class SingletonHelper {
        private static final DatabaseManager INSTANCE = new DatabaseManager();
    }
 
    public static DatabaseManager getInstance() {
        return SingletonHelper.INSTANCE;
    }
}

DCL Singleton Implementation

class DatabaseManager {
    private static volatile DatabaseManager instance; // Volatile ensures visibility across threads
 
    private DatabaseManager() {
        System.out.println("DatabaseManager initialized.");
    }
 
    public static DatabaseManager getInstance() {
        if (instance == null) {  // First check (No locking)
            synchronized (DatabaseManager.class) {  // Lock only the first time
                if (instance == null) {  // Second check (Ensures only one instance)
                    instance = new DatabaseManager();
                }
            }
        }
        return instance;
    }
}
 
// Client Code
public class DCLSingletonExample {
    public static void main(String[] args) {
        DatabaseManager db1 = DatabaseManager.getInstance();
        DatabaseManager db2 = DatabaseManager.getInstance();
 
        System.out.println(db1 == db2); // Output: true (Same instance)
    }
}

Enum Singleton

enum DatabaseManager {
    INSTANCE; // Single instance
 
    // Constructor (called only once)
    DatabaseManager() {
        System.out.println("DatabaseManager initialized.");
    }
 
    public void connect() {
        System.out.println("Connected to the database.");
    }
}
 
// Client Code
public class EnumSingletonExample {
    public static void main(String[] args) {
        DatabaseManager db1 = DatabaseManager.INSTANCE;
        db1.connect();
 
        DatabaseManager db2 = DatabaseManager.INSTANCE;
        db2.connect();
 
        System.out.println(db1 == db2); // Output: true (Same instance)
    }
}

Comparison of Singleton Implementation Methods in Java

Singleton TypeThread-Safe?Lazy Initialization?PerformanceReflection-Safe?Serialization-Safe?ComplexityBest Use Case
Basic (Lazy Initialization)❌ Noβœ… Yes⚑ Fast❌ No❌ No⭐ SimpleSingle-threaded apps
Synchronized Methodβœ… Yesβœ… Yes🐒 Slow (due to locking)❌ No❌ No⭐⭐ MediumMulti-threaded apps (small-scale)
Double-Checked Locking (DCL)βœ… Yesβœ… Yes⚑ Fast (locks only once)❌ No❌ No⭐⭐⭐ ModerateHigh-performance multi-threaded apps
Bill Pugh Singleton (Static Inner Class)βœ… Yesβœ… Yes⚑ Fast❌ No❌ No⭐⭐⭐ ModerateBest alternative to DCL
Enum Singleton (Recommended)βœ… Yes❌ No (Eager)⚑ Fastβœ… Yesβœ… Yes⭐⭐ SimpleBest for all cases (Preferred approach)

Tip

Lazy Initialization
The object is created only when it is needed (i.e., when it’s accessed for the first time).

Eager Initialization
The object is created as soon as the class is loaded, even if it is not used immediately.