Elegant and Efficient Singleton Implementations in Java

This article explores various Java singleton patterns—including eager initialization, static blocks, lazy initialization, double‑checked locking, volatile variables, static inner‑class holder, and serialization handling—explaining their mechanisms, thread‑safety concerns, and how to choose the most appropriate implementation for robust, high‑performance backend systems.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Elegant and Efficient Singleton Implementations in Java

When reading source code, developers often encounter singleton patterns used for resources such as database connections, thread pools, or Spring containers, and may wonder about their exact behavior and safety. This article discusses how to implement elegant, efficient, and data‑safe singletons in Java.

Simple eager initialization (getInstance)

Many classes expose a public static MySingleton getInstance() method that simply returns a pre‑created static instance. The code looks like:

public class MySingleton {
    private static MySingleton instance = new MySingleton();
    private MySingleton() {}
    public static MySingleton getInstance() {
        return instance;
    }
}

Clients use MySingleton singleton = MySingleton.getInstance();. The static field guarantees a single instance bound to the class.

Complex creation with static initialization block

When the singleton depends on other beans, a static block can enforce a strict serial order, preventing JVM reordering and ensuring thread safety:

public class MySingleton {
    private static MySingleton instance = null;
    private static OtherSingleton helper = null;
    static {
        helper = OtherSingleton.getInstance();
        instance = new MySingleton(helper);
    }
    private MySingleton(OtherSingleton helper) {
        if (helper) this.helper = helper;
        else throw new MyException("OtherSingleton getInstance failed");
    }
    public static MySingleton getInstance() {
        return instance;
    }
}

This guarantees that the dependent OtherSingleton is available before the main singleton is created.

Lazy ("懒汉式") initialization

The lazy approach creates the instance only when first requested:

public class MySingleton {
    private static MySingleton instance = null;
    private static OtherSingleton helper = null;
    private MySingleton(OtherSingleton helper) { /* same as above */ }
    public static MySingleton getInstance() {
        if (instance == null)
            instance = new MySingleton(OtherSingleton.getInstance());
        return instance;
    }
}

However, in a multithreaded environment two threads may simultaneously pass the if (instance == null) check, creating two instances.

Synchronizing the whole method

Adding synchronized to the method serializes access, but incurs a performance penalty because the lock is held for every call:

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

Double‑checked locking

To reduce lock contention, the method first checks without locking, then synchronizes only when the instance is still null:

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

Because of possible visibility issues, the instance field should be declared volatile to ensure that other threads see the fully constructed object.

Volatile‑based implementation

public class MySingleton {
    private volatile static MySingleton instance = null;
    private static OtherSingleton helper = null;
    private MySingleton(OtherSingleton helper) { /* same as above */ }
    public static MySingleton getInstance() {
        if (instance == null) {
            synchronized (MySingleton.class) {
                if (instance == null) {
                    instance = new MySingleton(OtherSingleton.getInstance());
                }
            }
        }
        return instance;
    }
}

Static inner‑class holder (Initialization‑on‑demand holder)

This technique leverages the JVM’s class‑loading guarantees to achieve lazy, thread‑safe initialization without explicit synchronization:

public class MySingleton {
    private MySingleton(OtherSingleton helper) { /* same as above */ }
    private static class MySingletonAdapter {
        private static final MySingleton instance = new MySingleton(OtherSingleton.getInstance());
    }
    public static MySingleton getInstance() {
        return MySingletonAdapter.instance;
    }
}

Serializable singleton

When a singleton implements Serializable, deserialization creates a new instance unless readResolve() returns the existing one:

public class MySingleton implements Serializable {
    private static final long serialVersionUID = -3453453414141241L;
    private static MySingleton instance = new MySingleton(OtherSingleton.getInstance());
    private MySingleton(OtherSingleton helper) { /* same as above */ }
    private Object readResolve() {
        return instance;
    }
}

Dependency‑injection (IoC) perspective

Complex dependency graphs can cause circular initialization problems; using an Inversion‑of‑Control container with dependency injection helps manage the creation order and resolve such cycles.

Overall, the article provides a comprehensive guide to selecting and implementing the most suitable singleton pattern in Java, balancing simplicity, performance, and thread safety.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

thread safetydesign patternSingleton
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.