Superman Problem - Singleton Pattern
🧠 Problem Statement
Section titled “🧠 Problem Statement”You are designing a library of superheroes for a video game. The library should ensure that only a single instance of any superhero (e.g., Superman) is created and shared across all consumers.
For example:
- There should only be one Superman in the game.
- Multiple threads or developers should not be able to create multiple instances of Superman.
❗ Constraints
Section titled “❗ Constraints”- Single Instance: Only one instance of the class should exist
- Thread Safety: Must work correctly in multi-threaded environments
- Global Access: The instance must be accessible from anywhere in the application
- Lazy Initialization: Instance should be created only when needed (optional but preferred)
- Performance: Should not significantly impact performance
📥 Input / Output
Section titled “📥 Input / Output”Input:
- Multiple threads calling
getInstance()method - Class loading and instantiation requests
Output:
- Single instance of the class shared across all threads
- Consistent behavior regardless of when
getInstance()is called
💡 Intuition About Solving This Problem
Section titled “💡 Intuition About Solving This Problem”The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. The key challenge is maintaining this single instance guarantee in a multi-threaded environment where multiple threads might try to create the instance simultaneously.
🚀 Steps to Take to Solve
Section titled “🚀 Steps to Take to Solve”- Make constructor private to prevent external instantiation
- Create a private static instance variable to hold the single instance
- Implement a public static getInstance() method to provide access
- Ensure thread safety using synchronization or volatile keywords
- Handle lazy initialization to create instance only when needed
- Consider performance implications of different synchronization approaches
⚠️ Edge Cases to Consider
Section titled “⚠️ Edge Cases to Consider”- Race conditions when multiple threads call getInstance() simultaneously
- Partially constructed objects due to Java memory model optimizations
- Class loading issues in different JVM implementations
- Serialization breaking singleton guarantee
- Reflection bypassing private constructor
- Performance degradation from excessive synchronization
💻 Code (with comments)
Section titled “💻 Code (with comments)”1. Naive Singleton (Eager Initialization)
Section titled “1. Naive Singleton (Eager Initialization)”public class SupermanNaiveButCorrect {
// Eagerly initialize the instance private static SupermanNaiveButCorrect superman = new SupermanNaiveButCorrect();
// Private constructor to prevent instantiation private SupermanNaiveButCorrect() { }
// Public method to return the single instance public static SupermanNaiveButCorrect getInstance() { return superman; }
// Object method public void fly() { System.out.println("I am Superman & I can fly!"); }}2. Lazy Initialization (With Flaws)
Section titled “2. Lazy Initialization (With Flaws)”public class SupermanWithFlaws {
private static SupermanWithFlaws superman;
private SupermanWithFlaws() { }
public static SupermanWithFlaws getInstance() { if (superman == null) { superman = new SupermanWithFlaws(); } return superman; }
public void fly() { System.out.println("I am Superman & I can fly!"); }}3. Thread-Safe Singleton (Synchronized)
Section titled “3. Thread-Safe Singleton (Synchronized)”public class SupermanCorrectButSlow {
private static SupermanCorrectButSlow superman;
private SupermanCorrectButSlow() { }
public static synchronized SupermanCorrectButSlow getInstance() { if (superman == null) { superman = new SupermanCorrectButSlow(); } return superman; }
public void fly() { System.out.println("I am Superman & I can fly!"); }}4. Double-Checked Locking (DCL)
Section titled “4. Double-Checked Locking (DCL)”public class SupermanSlightlyBetter {
private static SupermanSlightlyBetter superman;
private SupermanSlightlyBetter() { }
public static SupermanSlightlyBetter getInstance() { if (superman == null) { // First check (without synchronization) synchronized (SupermanSlightlyBetter.class) { if (superman == null) { // Second check (with synchronization) superman = new SupermanSlightlyBetter(); } } } return superman; }
public void fly() { System.out.println("I am Superman & I can fly!"); }}5. Correct Double-Checked Locking (Using volatile)
Section titled “5. Correct Double-Checked Locking (Using volatile)”public class Superman {
// Volatile ensures visibility and prevents reordering private static volatile Superman superman;
private Superman() { }
public static Superman getInstance() { if (superman == null) { // First check (without synchronization) synchronized (Superman.class) { if (superman == null) { // Second check (with synchronization) superman = new Superman(); } } } return superman; }
public void fly() { System.out.println("I am Superman & I can fly!"); }}🔍 Dry Run Example
Section titled “🔍 Dry Run Example”Scenario: Two threads calling getInstance() simultaneously
Thread 1 and Thread 2 both call getInstance() at the same time:
-
Initial State:
superman = null -
Thread 1:
- Checks
if (superman == null)→ true - Enters synchronized block
- Checks again
if (superman == null)→ true - Creates new instance:
superman = new Superman() - Returns instance
- Checks
-
Thread 2:
- Checks
if (superman == null)→ false (instance already created) - Returns existing instance without entering synchronized block
- Checks
Result: Only one instance is created, both threads get the same instance.
⏱️ Time Complexity / Space Complexity
Section titled “⏱️ Time Complexity / Space Complexity”Time Complexity:
- Eager Initialization: O(1) - instance created at class loading
- Lazy Initialization: O(1) after first creation, O(1) for subsequent calls
- Synchronized: O(1) but with synchronization overhead
- Double-Checked Locking: O(1) with minimal synchronization overhead
Space Complexity:
- All implementations: O(1) - only one instance stored
🔑 Key Challenges
Section titled “🔑 Key Challenges”| Challenge | Description |
|---|---|
| 🔁 Race Conditions | Multiple threads creating instances simultaneously |
| 🧵 Thread Safety | Ensuring single instance in multi-threaded environment |
| ⚡ Performance | Minimizing synchronization overhead |
| 🏗️ Memory Model | Handling Java’s memory model complexities |
✅ Solution: Singleton Pattern
Section titled “✅ Solution: Singleton Pattern”💡 Idea
Section titled “💡 Idea”Use the Singleton Pattern with appropriate synchronization to ensure:
- Only one instance of the class is created during the application’s lifetime.
- The same instance is returned to all requesting consumers.
- Thread safety is maintained without significant performance impact.
🔍 Why Does This Work?
Section titled “🔍 Why Does This Work?”- 🚫 Private constructor prevents external instantiation
- 🔒 Synchronization ensures thread safety during creation
- 🔄 Double-checking minimizes synchronization overhead
- 📍 Static instance provides global access point
👩🔬 Real-World Analogy
Section titled “👩🔬 Real-World Analogy”Think of a government office that can only have one director. No matter how many people ask for the director, they always get the same person. The office ensures only one director exists and provides a consistent way to access them.
💡 Pro Tips for Interviews
Section titled “💡 Pro Tips for Interviews”- Always mention thread safety considerations
- Discuss the trade-offs between different implementations
- Explain why volatile is needed in double-checked locking
- Consider alternative patterns like enum singletons
- Mention serialization and reflection challenges
✅ Summary
Section titled “✅ Summary”| ✅ Feature | Covered |
|---|---|
| Thread-safe | ✅ Yes |
| Single instance | ✅ Yes |
| Performance | ✅ Yes |
| Lazy loading | ✅ Yes |
Comparison of Implementations
Section titled “Comparison of Implementations”| Implementation | Thread-Safe | Lazy Initialization | Performance | Complexity |
|---|---|---|---|---|
| Naive Singleton | No | No | High (Eager Loading) | Simple |
| Lazy Initialization (Flawed) | No | Yes | High | Simple |
| Thread-Safe Singleton | Yes | Yes | Low (Synchronized) | Moderate |
| Double-Checked Locking (DCL) | Yes | Yes | High | Complex |
Correct DCL (With volatile) | Yes | Yes | High | Moderate |