Implementation Methods
Several ways to implement Singleton, each with different trade-offs.
Eager Initialization
public class DatabaseConnection {
private static final DatabaseConnection INSTANCE = new DatabaseConnection();
private DatabaseConnection() { }
public static DatabaseConnection getInstance() {
return INSTANCE;
}
}
- Simple, thread-safe
- Instance created at class loading
- Good if instance is always needed
Lazy Initialization
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() { }
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
- Created only when needed
- NOT thread-safe
Synchronized Lazy
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() { }
public static synchronized DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
- Thread-safe
- Performance overhead from synchronization
Double-Checked Locking
public class DatabaseConnection {
private static volatile DatabaseConnection instance;
private DatabaseConnection() { }
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
}
- Thread-safe, no sync after initialization
- Uses volatile for memory visibility
Bill Pugh Singleton
public class DatabaseConnection {
private DatabaseConnection() { }
private static class Holder {
private static final DatabaseConnection INSTANCE =
new DatabaseConnection();
}
public static DatabaseConnection getInstance() {
return Holder.INSTANCE;
}
}
- Lazy, thread-safe, no synchronization
- Best approach in most cases
Thread Safety
Thread safety is critical for Singleton in multi-threaded environments.
The Problem
Thread A Thread B
──────── ────────
check if instance == null
→ true
check if instance == null
→ true
create new instance
create new instance
Two instances created! ❌
Solutions Comparison
| Method | Thread-Safe | Lazy | Performance |
|---|---|---|---|
| Eager | ✓ | ✗ | Fast |
| Synchronized | ✓ | ✓ | Slow |
| Double-Checked | ✓ | ✓ | Fast after init |
| Bill Pugh | ✓ | ✓ | Fast |
| Enum | ✓ | ✗ | Fast |
Enum Singleton
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
DatabaseConnection() {
this.connection = createConnection();
}
public Connection getConnection() {
return connection;
}
}
// Usage
Connection conn = DatabaseConnection.INSTANCE.getConnection();
- Thread-safe by JVM guarantee
- Handles serialization automatically
- Prevents reflection attacks
Testing Singleton
// Problem: Singleton makes testing hard
public class OrderService {
private final DatabaseConnection db = DatabaseConnection.getInstance();
// Can't mock db in tests!
}
// Solution: Dependency Injection
public class OrderService {
private final DatabaseConnection db;
public OrderService(DatabaseConnection db) {
this.db = db;
}
// Can inject mock in tests
}
// In production: inject singleton
OrderService service = new OrderService(DatabaseConnection.getInstance());
// In test:
OrderService service = new OrderService(mockDb);
Singleton Anti-Patterns
- Global state: Singleton is global state, hard to reason about
- Hidden dependencies: Classes secretly depend on singleton
- Testing difficulty: Can't mock without DI
- Tight coupling: Classes coupled to singleton implementation
- Lifetime management: Hard to control when singleton is created/destroyed
When to Use
Singleton is often overused. Understanding when it's appropriate is crucial.
Legitimate Use Cases
| Use Case | Why Singleton |
|---|---|
| Database connection pool | Shared pool across application |
| Thread pool | Single thread pool managing threads |
| Configuration manager | Single source of truth for config |
| Cache | Shared cache across components |
| Logger | Single logging instance |
When NOT to Use Singleton
| Situation | Why Not |
|---|---|
| Just for convenience | Use DI instead |
| To share state | Use dependency injection |
| For database access | Use repository pattern |
| For any service | Use DI container |
Singleton vs Dependency Injection
// Singleton: hidden dependency
public class OrderService {
private Database db = Database.getInstance(); // Hidden!
public void createOrder(Order order) {
db.save(order); // Where does db come from?
}
}
// Dependency Injection: explicit dependency
public class OrderService {
private final Database db;
public OrderService(Database db) { // Explicit!
this.db = db;
}
public void createOrder(Order order) {
db.save(order); // Clear dependency
}
}
Modern Alternative: DI Container
// Spring/Guice manage singleton scope
@Service // Singleton scope by default
public class DatabaseService {
public Connection getConnection() { ... }
}
@Service
public class OrderService {
private final DatabaseService db;
@Autowired
public OrderService(DatabaseService db) {
this.db = db; // Injected by container
}
}
Decision Guide
Do you need exactly one instance?
├── Yes: Is it for infrastructure (connection pool, thread pool)?
│ └── Yes: Singleton is OK
├── Yes: Is it for business logic?
│ └── Yes: Use DI with singleton scope
└── No: Don't use Singleton
Can you use DI container?
├── Yes: Use DI with singleton scope
└── No: Consider Singleton implementation carefully
Practice Problems
Design a scalable Singleton Pattern system. Cover high-level architecture, data model, and API design.
Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliabilityHow would you scale Singleton Pattern to handle 10x the current load? Identify bottlenecks and solutions.
Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decompositionAnalyze potential failure modes for Singleton Pattern and design mitigation strategies.
Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradationQuiz
1. What is the Singleton pattern?
2. What is the Bill Pugh Singleton approach?
3. Why is Singleton considered an anti-pattern?
4. What is the modern alternative to Singleton?
5. When is Singleton appropriate?
Flashcards
Question
What is the Singleton pattern?
Click to reveal answer
Answer
Ensures a class has only one instance and provides a global point of access. Used for shared resources like connection pools.
Question
What is the best Singleton implementation?
Click to reveal answer
Answer
Bill Pugh Singleton using static inner class holder. Lazy, thread-safe, no synchronization overhead.
Question
Why is Singleton considered an anti-pattern?
Click to reveal answer
Answer
Creates global state, hidden dependencies, tight coupling, and testing difficulties. DI with singleton scope is preferred.
Question
Singleton vs DI with singleton scope?
Click to reveal answer
Answer
Singleton: hidden dependency, global state. DI: explicit dependencies, testable, loose coupling. DI is preferred.
Question
When to use Singleton?
Click to reveal answer
Answer
For infrastructure components that genuinely need one instance: connection pools, thread pools, configuration managers, caches.
Revision Notes
Key Takeaways
- 1.Singleton ensures one instance with global access
- 2.Bill Pugh Singleton is the recommended implementation
- 3.Singleton creates global state and hidden dependencies
- 4.DI with singleton scope is the modern alternative
- 5.Use Singleton only for genuine infrastructure needs
Interview Tips
- •Explain when Singleton is appropriate vs when to use DI
- •Discuss thread safety concerns in Singleton implementation
- •Mention the testing difficulties caused by Singleton
- •Show how DI container achieves singleton scope without global state
Cheat Sheet
Singleton Pattern - Cheat Sheet
Implementation Methods:
| Method | Thread-Safe | Lazy | Best For |
|---|---|---|---|
| Eager | ✓ | ✗ | Always needed |
| Synchronized | ✓ | ✓ | Simple cases |
| Double-Checked | ✓ | ✓ | Performance |
| Bill Pugh | ✓ | ✓ | Most cases |
| Enum | ✓ | ✗ | Serialization |
Bill Pugh (Recommended):
private static class Holder {
static final INSTANCE = new Singleton();
}
Problems:
- Global state
- Hidden dependencies
- Testing difficulty
- Tight coupling
Modern Alternative:
DI container with singleton scope
- Explicit dependencies
- Testable
- Loose coupling