ProgrammingJava backend developer

How to implement the Singleton pattern in Java considering multithreading and serialization? What are the nuances of implementation?

Pass interviews with Hintsage AI assistant

Answer

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:

  1. Thread-safe implementation (Double-checked Locking): Often used for lazy initialization.
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; } }
  1. Enum Singleton: The best way in terms of protection against serialization, reflection, and multithreading.
enum EnumSingleton { INSTANCE; // methods }
  1. Serialization issues: A regular Singleton may lose the uniqueness of the instance after serialization/deserialization. To work correctly, you need to add the readResolve() method:
private Object readResolve() { return getInstance(); }

Trick question

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 volatile to 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.