# Mastering the Singleton Pattern in Java

The **Singleton pattern** ensures that a class has **only one instance** and provides a way to access it from anywhere in the code.

### ✅ When to use it:

* You need **a single, shared object** — like an app-wide settings manager, log writer, or clock.
    

### ❌ When not to use it:

* You need **multiple independent instances** (e.g., one per user or request).
    

---

### 🔤 Basic Implementation (Eager, Thread-Safe)

```java
public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return instance;
    }
}
```

Key points:

* A private constructor to prevent the creation of additional instances
    
* A single private instance that is made accessible in the getInstance method
    
* The instance is initialized when the class is loaded, thus, it is eager
    

### 🔤 Basic Implementation (Lazy, Not Thread-Safe)

```java
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
```

This lazy implementation instantiates the instance when the getInstance is invoked for the first time, thus it is lazy. This looks fine at first glance, but it **fails in multi-threaded environments**.

In a multi-threaded program, you can’t control how threads are scheduled. Two threads might call `getInstance()` at the same time, both see that `instance` is still `null`, and both proceed to create a new object. This results in **two different instances**, breaking the singleton rule.

This issue is called a **race condition** — and we’ll now look at thread-safe alternatives that avoid it.

---

## Real-World Examples of the Singleton Pattern

Many frameworks use the Singleton pattern to manage shared components.

### 📦 Spring Framework

By default, Spring creates beans as singletons. When you annotate a class with `@Component`, Spring instantiates the class once and reuses it throughout the application:

```css
javaCopyEdit@Component
public class MyService {
    // Singleton bean by default
}
```

### 🧪 JUnit

JUnit uses a singleton test runner internally to manage and coordinate test execution. This ensures all tests run in a consistent and isolated environment.

---

## Thread-Safe Singleton Implementations

Here are five common ways to implement a thread-safe singleton in Java — each with a short explanation, code sample, and its key advantage.

---

### 🔐 1. Eager Initialization (Simple, Thread-Safe)

```java
public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();
    private EagerSingleton() {}
    public static EagerSingleton getInstance() {
        return instance;
    }
}
```

**✅ Advantage**: Very simple and thread-safe because the instance is created when the class loads.  
**❌ Downside**: The instance is created even if it's never used.

---

### 🧵 2. Synchronized Method (Safe but Slow)

```java
public class SynchronizedSingleton {
    private static SynchronizedSingleton instance;
    private SynchronizedSingleton() {}
    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}
```

**✅ Advantage**: Easy to write and understand.  
**❌ Downside**: Synchronization happens on every call, which can hurt performance.

---

### 🌀 3. Double-Checked Locking (Efficient & Lazy)

```java
public class DoubleCheckedSingleton {
    private static volatile DoubleCheckedSingleton instance;
    private DoubleCheckedSingleton() {}
    public static DoubleCheckedSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckedSingleton();
                }
            }
        }
        return instance;
    }
}
```

**✅ Advantage**: Thread-safe, lazy, and avoids unnecessary locking.  
**⚠️ Downside**: Slightly more complex to write and understand.

---

### 📥 4. Static Inner Class (Elegant & Lazy)

```java
public class InnerClassSingleton {
    private InnerClassSingleton() {}
    private static class Holder {
        private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }
    public static InnerClassSingleton getInstance() {
        return Holder.INSTANCE;
    }
}
```

**✅ Advantage**: Thread-safe, lazy, and no synchronization overhead.  
**❌ Downside**: Still vulnerable to reflection and serialization attacks.

---

### 🛡️ 5. Enum Singleton (Most Robust)

```java
public enum EnumSingleton {
    INSTANCE;
    public void doSomething() {
        // ...
    }
}
```

**✅ Advantage**: Thread-safe, lazy, and fully protected against reflection and serialization attacks.  
**🏆 This is the safest and most recommended approach, especially in libraries or critical systems.**  
**📘 Recommended in *Effective Java* by Joshua Bloch.**

## Summary: Best Thread-Safe Singleton Approaches

| Approach | Lazy? | Robustness | Performance |
| --- | --- | --- | --- |
| 🏆 **Enum Singleton** | ✅ | ✅ Fully protected | ✅ Excellent |
| Static Inner Class | ✅ | ❌ Vulnerable to reflection | ✅ Excellent |
| Double-Checked Locking | ✅ | ❌ Vulnerable to reflection | ✅ Good |
| Eager Initialization | ❌ | ❌ Vulnerable to reflection | ✅ Excellent |
| Synchronized Method | ✅ | ❌ Vulnerable to reflection | ❌ Slower |

## **⚠️** Disadvantages of the Singleton Pattern

While the Singleton pattern can be useful, it comes with several **important disadvantages**, especially when overused or misunderstood:

* **Global state**: Acts like a global variable, making code harder to manage and test.
    
* **Hard to test**: Difficult to mock or replace in unit tests.
    
* **Breaks SOLID principles**: Often violates Single Responsibility and Dependency Inversion.
    
* **Concurrency issues**: Poor implementation can create multiple instances in multi-threaded apps.
    
* **Long-lived lifecycle**: Can lead to memory leaks or outdated state.
    
* **Tight coupling**: Makes refactoring or replacing the singleton harder over time.
    

## Conclusion

The Singleton pattern is a simple and effective tool for ensuring that a class has only one instance and a global point of access. It’s useful for managing shared resources like configuration, logging, or system-wide coordination.

Understanding the trade-offs between different implementations will help you choose the safest and most efficient approach for your needs — and avoid common pitfalls along the way.

We recommend the **enum-based singleton** for its simplicity and robustness. It’s inherently thread-safe, lazy, and protected against reflection and serialization attacks, making it the safest choice in most scenarios. It’s also the approach recommended by *Effective Java* author Joshua Bloch.
