Browse Java Design Patterns & Enterprise Application Architecture

Thread-Safe Singleton Implementations in Java

Compare thread-safe Singleton implementation styles in Java and choose the smallest one that matches the lifecycle requirement.

Thread-safe singleton: A singleton implementation whose construction and publication remain correct when multiple threads access it concurrently.

Java offers several safe ways to implement a singleton, but they are not interchangeable in readability or lifecycle behavior. The best one depends on whether you need eager creation, lazy creation, or container-managed ownership.

Eager Initialization Is The Simplest Safe Form

1public final class MetricsRegistry {
2    private static final MetricsRegistry INSTANCE = new MetricsRegistry();
3
4    private MetricsRegistry() {}
5
6    public static MetricsRegistry getInstance() {
7        return INSTANCE;
8    }
9}

This is thread-safe because class initialization is handled safely by the JVM. If eager construction is acceptable, this is often the clearest implementation.

Synchronized Accessor Works, But Usually Reads Poorly

 1public final class SettingsStore {
 2    private static SettingsStore instance;
 3
 4    private SettingsStore() {}
 5
 6    public static synchronized SettingsStore getInstance() {
 7        if (instance == null) {
 8            instance = new SettingsStore();
 9        }
10        return instance;
11    }
12}

This is correct, but it adds synchronization to every access. In many modern Java codebases, it is more often a teaching example than the best production default.

Double-Checked Locking Is Valid, But Easy To Overvalue

 1public final class ConnectionManager {
 2    private static volatile ConnectionManager instance;
 3
 4    private ConnectionManager() {}
 5
 6    public static ConnectionManager getInstance() {
 7        if (instance == null) {
 8            synchronized (ConnectionManager.class) {
 9                if (instance == null) {
10                    instance = new ConnectionManager();
11                }
12            }
13        }
14        return instance;
15    }
16}

With volatile, this is valid in modern Java. But it is also more complex than the eager version or the holder idiom. Use it only when lazy initialization really matters and simpler approaches do not fit.

The Holder Idiom Is Often The Best Lazy Option

 1public final class AuditSink {
 2    private AuditSink() {}
 3
 4    private static final class Holder {
 5        private static final AuditSink INSTANCE = new AuditSink();
 6    }
 7
 8    public static AuditSink getInstance() {
 9        return Holder.INSTANCE;
10    }
11}

This gives you lazy initialization with JVM-backed class-loading safety and less concurrency noise than double-checked locking.

Choose By Lifecycle, Not By Cleverness

The right question is not “which version is most advanced?” It is:

  • Do I need lazy construction?
  • Is eager startup acceptable?
  • Would an enum or container-managed singleton be cleaner?

If eager construction is fine, take the eager version. If laziness matters, the holder idiom is often clearer than hand-written lock choreography.

Design Review Questions

When reviewing thread-safe singleton code, ask:

  • Is lazy initialization actually required?
  • Is the implementation more complex than the lifecycle justifies?
  • Would the holder idiom or enum be clearer?
  • Is the singleton hiding a broader dependency problem?

Correctness matters, but clarity matters too. Thread safety should not become a trophy implementation.

Loading quiz…
Revised on Thursday, April 23, 2026