The pattern is useful when exactly one object should coordinate shared resources or application-wide behaviour. Common examples include configuration managers, logging services, cache managers, and thread pools.
Instead of allowing clients to create multiple objects using constructors, the Singleton Pattern hides object creation and provides a static method that always returns the same instance.
The Singleton Pattern is one of the simplest design patterns, but it must be implemented carefully to ensure thread safety in multi-threaded applications.
Structure
The Singleton Pattern consists of the following participants.1. The Singleton class contains a private constructor that prevents external object creation.
2. The Singleton maintains a single static instance of itself.
3. The Client accesses the object through a public static method instead of creating it directly.

Java Implementation
Suppose an application has a configuration manager that loads application properties during startup.Since the configuration should be loaded only once and shared across the application, the Singleton Pattern is an ideal choice. Create the singleton class.
public class ConfigurationManager {
private static final ConfigurationManager INSTANCE = new ConfigurationManager();
private ConfigurationManager() {
}
public static ConfigurationManager getInstance() {
return INSTANCE;
}
public void printConfiguration() {
System.out.println("Application configuration loaded.");
}
}
The client retrieves the singleton instance whenever it is needed.
public class Main {
public static void main(String[] args) {
ConfigurationManager manager1 =
ConfigurationManager.getInstance();
ConfigurationManager manager2 =
ConfigurationManager.getInstance();
manager1.printConfiguration();
System.out.println(manager1 == manager2);
}
}
Output:
Application configuration loaded.
true
How It Works?
The constructor of ConfigurationManager is declared as private, preventing other classes from creating new instances using the new keyword.A single instance of the class is created when the class is loaded and stored in the static INSTANCE field. Every call to getInstance() returns this same object.
Since every client receives the identical instance, all parts of the application share the same configuration manager. This guarantees consistent behaviour while avoiding unnecessary object creation.
Using eager initialization, as shown in this example, makes the implementation naturally thread-safe because the JVM guarantees that class initialization occurs only once.
Other Singleton Implementations
The example shown above demonstrate the recommended approach for implementing the Singleton pattern.However, several other implementations are also commonly encountered in Java. Each provides a different balance between simplicity, thread safety, and performance.
Lazy Initialization
The simplest lazy initialization approach delays object creation until the first request for the singleton instance.public final class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
This implementation is easy to understand and avoids creating the object until it is actually needed.
However, it is not thread-safe. If multiple threads invoke getInstance() simultaneously, more than one instance may be created.
Synchronized Method
A simple way to make lazy initialization thread-safe is to synchronize the entire getInstance() method.public final class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
This implementation guarantees that only one thread can initialize the singleton instance.
Although it is thread-safe, every invocation of getInstance() acquires the synchronization lock, even after the singleton has already been created.
This unnecessary synchronization may reduce performance in highly concurrent applications.
Double-Checked Locking
The Double-Checked Locking (DCL) pattern reduces synchronization overhead by locking only during the first initialization.public final 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;
}
}
The first null check avoids synchronization after the singleton has been created, while the second check prevents multiple threads from creating separate instances simultaneously.
The volatile keyword is essential because it prevents instruction reordering and ensures that all threads observe a fully initialized object.
Initialization-on-Demand Holder (IoDH)
The Initialization-on-Demand Holder (IoDH) idiom is generally considered the preferred lazy singleton implementation.It relies on the JVM's class-loading mechanism to provide thread safety without explicit synchronization.
public final class Singleton {
private Singleton() {
}
private static class Holder {
private static final Singleton INSTANCE =
new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
The nested Holder class is not loaded until getInstance() is called for the first time.
The JVM guarantees that class initialization is thread-safe, ensuring that the singleton instance is created only once without any synchronization overhead.
This implementation combines lazy initialization, thread safety, excellent performance, and simple code, making it the preferred choice for most applications.
Serialization Can Break Singleton?
When a singleton object is serialized and later deserialized, the JVM creates a new object instead of returning the existing singleton instance.Singleton singleton1 = Singleton.getInstance();
ObjectOutputStream out =
new ObjectOutputStream(
new FileOutputStream("singleton.ser")
);
out.writeObject(singleton1);
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream("singleton.ser")
);
Singleton singleton2 =
(Singleton) in.readObject();
System.out.println(singleton1 == singleton2);
Output:
false
Instead of returning the existing singleton instance, deserialization constructs a new object, violating the Singleton pattern.
Solution
Implement the readResolve() method.private Object readResolve() {
return getInstance();
}
The JVM automatically invokes readResolve() after deserialization and replaces the newly created object with the existing singleton instance.
Reflection Can Break Singleton?
Reflection can invoke a private constructor, allowing multiple singleton instances to be created.Constructor constructor =
Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton1 =
Singleton.getInstance();
Singleton singleton2 =
constructor.newInstance();
System.out.println(singleton1 == singleton2);
Output:
false
Even though the constructor is private, reflection bypasses the access check.
Solution
Guard the constructor.public final class Singleton {
private static boolean initialized = false;
private Singleton() {
if (initialized) {
throw new RuntimeException(
"Singleton already initialized."
);
}
initialized = true;
}
// getInstance()...
}
If reflection attempts to invoke the constructor after the singleton has been created, the constructor throws an exception.
Note: This protection is not foolproof. A determined attacker with sufficient privileges can still bypass it using advanced reflection or low-level JVM APIs.
Best Protection (Enum Singleton)
The most robust singleton implementation in Java uses an enum.public enum Singleton {
INSTANCE;
}
An enum singleton is:
- Thread-safe
- Lazy enough for most applications (initialized when the enum is first used)
- Immune to serialization attacks
- Immune to reflection attacks (the JVM prevents reflective creation of enum instances)
For these reasons, Effective Java by Joshua Bloch recommends the enum-based singleton as the preferred implementation whenever it fits the application's requirements.
Java serialization treats enums specially by serializing only the name of the enum constant, not the object itself.
During deserialization, the JVM returns the existing enum constant instead of creating a new instance, preserving the Singleton property automatically.
Advantages
1. It guarantees that only one instance of a class exists.2. It provides a global access point to shared resources.
3. It avoids unnecessary object creation.
4. The eager initialization approach is inherently thread-safe.
Disadvantages
1. It introduces global state, which can make applications harder to test.2. It can create hidden dependencies between classes.
3. Lazy initialization requires additional synchronization to ensure thread safety.
4. Excessive use of singletons can reduce flexibility and violate the Single Responsibility Principle.
JDK Examples
The Singleton Pattern appears throughout the Java platform.1. The java.lang.Runtime class exposes a single JVM runtime instance through the Runtime.getRuntime() method.
2. The java.awt.Desktop class provides access to the desktop environment through the Desktop.getDesktop() method.
Summary
The Singleton Pattern ensures that a class has exactly one instance while providing a global point of access to it.It is commonly used for shared services such as configuration managers, loggers, and caches.
When implemented correctly, the Singleton Pattern provides controlled access to shared resources while preventing multiple instances from being created.