Skip to content
On this page

Singleton

Ensure that a class has only one instance and provide a global access point to it.

The Singleton pattern aims to guarantee that a class has only one instance throughout the lifecycle of a process.

Because this class has only one instance, clients naturally cannot use new Xyz() to create instances. Therefore, the constructor of a Singleton must be private to prevent external instantiation. However, within the class itself, a static field can hold the sole instance:

java
public class Singleton {
    // Static field holding the sole instance:
    private static final Singleton INSTANCE = new Singleton();

    // Private constructor to prevent external instantiation:
    private Singleton() {
    }
}

So, the question arises: how does an external client obtain this unique instance?

The answer is to provide a static method that directly returns the instance:

java
public class Singleton {
    // Static field holding the sole instance:
    private static final Singleton INSTANCE = new Singleton();

    // Static method to return the instance:
    public static Singleton getInstance() {
        return INSTANCE;
    }

    // Private constructor to prevent external instantiation:
    private Singleton() {
    }
}

Alternatively, you can expose the static variable directly:

java
public class Singleton {
    // Static field holding the sole instance:
    public static final Singleton INSTANCE = new Singleton();

    // Private constructor to prevent external instantiation:
    private Singleton() {
    }
}

Thus, implementing the Singleton pattern is straightforward:

  1. Private Constructor: Ensure the constructor is private to prevent external instantiation.
  2. Private Static Variable: Hold the sole instance in a private static variable to guarantee global uniqueness.
  3. Public Static Method: Provide a public static method to return the unique instance, allowing external clients to access it.

Some classes in the Java Standard Library are singletons. For example, the Runtime class:

java
Runtime runtime = Runtime.getRuntime();

Some students might have heard about lazy initialization, where the global unique instance is initialized only when the caller first invokes getInstance(), like this:

java
public class Singleton {
    private static Singleton INSTANCE = null;

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }

    private Singleton() {
    }
}

Unfortunately, this approach is incorrect in a multithreaded context because it can create multiple instances under race conditions. To fix this, the entire method must be synchronized:

java
public synchronized static Singleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new Singleton();
    }
    return INSTANCE;
}

However, synchronization severely impacts concurrency performance. Some students have heard of double-checked locking, like this:

java
public static Singleton getInstance() {
    if (INSTANCE == null) {
        synchronized (Singleton.class) {
            if (INSTANCE == null) {
                INSTANCE = new Singleton();
            }
        }
    }
    return INSTANCE;
}

Nevertheless, due to Java's memory model, double-checked locking does not hold here. To truly implement lazy initialization, it must be done through Java's ClassLoader mechanism. If there are no special requirements, it's best not to use lazy initialization when implementing the Singleton pattern to keep the code simpler.

Another way to implement Singleton is using Java's enum, as Java guarantees that each enum constant is a singleton. Therefore, we only need to create an enum with a single instance:

java
public enum World {
    // The sole instance:
    INSTANCE;

    private String name = "world";

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Enum classes can also define their own fields and methods just like other classes. From the caller's perspective, the World class can be used as follows:

java
String name = World.INSTANCE.getName();

Using an enum to implement Singleton also avoids a potential issue with the first approach: serialization and deserialization can bypass the private constructor of a regular class, creating multiple instances. Enum classes do not have this problem.

When Should We Use Singleton?

In reality, many programs, especially web applications, treat most service classes as singletons. If all classes were implemented using the Singleton pattern, it would be very cumbersome. Therefore, it's usually handled by frameworks (like Spring) that instantiate these classes as singletons by convention. Clients are expected to obtain instances through the framework rather than using the new operator:

java
@Component // Indicates a singleton component
public class MyService {
    ...
}

Thus, unless absolutely necessary, the Singleton pattern is generally implemented by convention rather than being explicitly enforced.

Practice

Implement two Singleton patterns to achieve a singleton.

java
// Eager Initialization Singleton
public class SingletonEager {
    // Static field holding the sole instance:
    private static final SingletonEager INSTANCE = new SingletonEager();

    // Private constructor to prevent external instantiation:
    private SingletonEager() {
    }

    // Static method to return the instance:
    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

// Enum Singleton
public enum SingletonEnum {
    INSTANCE;

    private String data;

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

// Usage Example:
public class Client {
    public static void main(String[] args) {
        // Using Eager Initialization Singleton
        SingletonEager singleton1 = SingletonEager.getInstance();
        SingletonEager singleton2 = SingletonEager.getInstance();
        System.out.println(singleton1 == singleton2); // true

        // Using Enum Singleton
        SingletonEnum enumSingleton1 = SingletonEnum.INSTANCE;
        SingletonEnum enumSingleton2 = SingletonEnum.INSTANCE;
        System.out.println(enumSingleton1 == enumSingleton2); // true
    }
}

Summary

The Singleton pattern ensures that a class has only one instance throughout the runtime of a program and provides a global access point to that instance.

Key Points:

  • Private Constructor: Prevents external instantiation, ensuring only one instance exists.
  • Private Static Variable: Holds the sole instance, guaranteeing global uniqueness.
  • Public Static Method: Provides a way for external clients to access the unique instance.
  • Eager vs. Lazy Initialization: Eager initialization creates the instance at class loading time, while lazy initialization delays creation until it's needed. However, lazy initialization can introduce complexity in multithreaded environments.
  • Enum-Based Singleton: Using Java enum types is a simple and effective way to implement Singleton, avoiding issues related to serialization and reflection.

Java frameworks like Spring typically manage Singleton instances by convention, allowing developers to treat service classes as singletons without manually implementing the pattern. This approach simplifies code management and leverages the framework's capabilities to ensure singleton behavior.

The Singleton pattern is particularly useful for managing shared resources, configurations, or any scenario where a single point of access is essential.

Singleton has loaded