Thread-Safe Singleton
Ensure exactly one instance is created across multiple threads, with lazy initialization and no unnecessary synchronization on the hot path.
Thread-Safe Singleton = building a statue in a town square while 100 workers watch. You need to make sure only ONE statue gets built, even if 10 workers start at the same time. Double-check the pedestal is empty, THEN lock the crane.
Three correct implementations: (1) Initialization-on-demand holder: private static class Holder { static final T INSTANCE = new T(); } — JVM class loading is lazy and thread-safe, zero synchronization overhead. (2) Enum Singleton: enum T { INSTANCE; } — reflection-safe, serialization-safe, JVM-guaranteed. (3) Double-checked locking with volatile: check null, synchronize, check null again, instantiate — volatile prevents partial construction visibility.
The broken DCL pattern (without volatile) was the source of countless subtle bugs pre-Java 5. The issue: in the Java Memory Model before JSR-133, memory writes could be reordered by the JVM. Thread A could set the instance reference before the constructor had finished — Thread B would see a non-null but incompletely initialized object. The volatile keyword (JSR-133 semantics) establishes a happens-before relationship: the write to the instance field (after full construction) happens-before any subsequent read by another thread. The enum Singleton is the safest: enum values are initialized by the class loader, which is thread-safe; Java's serialization mechanism guarantees enum singletons survive deserialization (serialized enums are resolved to the existing constant); and reflection cannot create new enum instances (IllegalArgumentException). In modern code, prefer DI container scoping over manual Singleton — Spring's @Bean with no scope annotation defaults to singleton scope, managed by the container.
My preferred thread-safe Singleton is the initialization-on-demand holder idiom: a private static nested class holds the instance field, initialized at class load time by the JVM. The JVM guarantees class loading is atomic and lazy — no synchronization needed. My second choice for serialization-safety is an enum Singleton. I avoid the double-checked locking pattern in new code — it requires volatile and is harder to reason about than the holder idiom. In production systems, I'd replace manual Singleton with a DI container singleton scope — it's testable, mockable, and the lifecycle is managed by the framework.
The most common mistake is synchronized getInstance() — correct but synchronizes on every single call, killing throughput. The second mistake is DCL without volatile — looks correct, fails under the Java Memory Model due to instruction reordering.