The classic Singleton guarantees the creation of only one instance of an object. In Java, there are several ways to implement it, but considering multithreading and serialization, you need to take into account certain nuances:
public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
enum EnumSingleton { INSTANCE; // methods }
private Object readResolve() { return getInstance(); }
Is it enough to use a synchronized method getInstance() for thread-safe Singleton?
Answer: Yes, but this approach leads to reduced performance because synchronized is called on every access to getInstance. More efficient is Double-checked Locking + volatile for instance, or using Enum for Singleton implementation.
Example of inefficient code:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
Story
In a financial system, when implementing the singleton, they forgot to add
volatileto the instance reference in double-checked locking. As a result, under high loads, random instances of the class were created, leading to inconsistency in reporting.
Story
In a logging library, Singleton was implemented through a private static object, however, during serialization and subsequent deserialization (for example, in a clustered environment), multiple instances occurred. The problem was solved by adding readResolve().
Story
In an analytics system, a developer implemented a thread-safe Singleton through a synchronized method getInstance(). In a high-load system during peak times, there was a sharp drop in performance; it turned out that calls to getInstance() (thousands of times per second) were blocking each other due to unnecessary synchronization, although initialization was only needed once.