Creational Patterns • Singleton PatternHard⏱️ ~3 min
Singleton Pattern: Common Interview Questions
Question 1: How do you make Singleton thread-safe?
Answer: Three approaches, each with trade-offs:
First, Eager Initialization: Create instance at class load time before any threads run. Thread-safe by default because class loading is synchronized by the class loader. Drawback: Instance created even if never used.
First, Eager Initialization: Create instance at class load time before any threads run. Thread-safe by default because class loading is synchronized by the class loader. Drawback: Instance created even if never used.
class Singleton:
instance = new Singleton() // at class load
public static getInstance():
return instance // always thread-safe
Second, Synchronized Method: Add synchronization to instance = new Singleton() // at class load
public static getInstance():
return instance // always thread-safe
getInstance() to prevent concurrent creation. Drawback: Every call acquires a lock, even after initialization completes, causing performance bottleneck.public static synchronized getInstance():
if instance is null:
instance = new Singleton()
return instance
Third, Double-Checked Locking: Check null without lock first (fast path). Only acquire lock if null, then check again inside lock (slow path). Critical: Instance variable must be marked volatile or equivalent to prevent instruction reordering. Best choice when lazy initialization is needed and performance matters.if instance is null:
instance = new Singleton()
return instance
instance = null // must be volatile
public static getInstance():
if instance is null: // first check (no lock)
lock():
if instance is null: // second check (with lock)
instance = new Singleton()
return instance
public static getInstance():
if instance is null: // first check (no lock)
lock():
if instance is null: // second check (with lock)
instance = new Singleton()
return instance
Interview Tip: Explain why double-checked locking needs two null checks. Without the second check inside the lock, two threads could both pass the first check, and both would create instances.
Question 2: How do you prevent Singleton from being cloned or serialized?
Cloning Attack: If Singleton implements
Solution: Override
Solution: Implement
Cloneable, calling clone() creates a second instance, violating the pattern.Solution: Override
clone() to throw an exception:public clone():
throw new CloneNotSupportedException("Cannot clone singleton")
Serialization Attack: Deserializing a serialized singleton creates a new instance because deserialization bypasses the constructor.throw new CloneNotSupportedException("Cannot clone singleton")
Solution: Implement
readResolve() method to return the existing instance:private readResolve():
return getInstance() // return existing instance
Better Approach: Use enum-based singleton (language-dependent). Enums prevent both cloning and serialization attacks by default and are inherently thread-safe.return getInstance() // return existing instance
Question 3: Design a ConfigurationManager as Singleton for a Library Management System.
Requirements: Load configuration once from file, provide settings to all components, support reloading without restart.
Design:
Design:
ConfigurationManager
- instance: ConfigurationManager
- settings: Map<String, String>
- filePath: String
- lastModified: Timestamp
- settings: Map<String, String>
- filePath: String
- lastModified: Timestamp
- ConfigurationManager()
+ getInstance(): ConfigurationManager
+ getSetting(key: String): String
+ setSetting(key: String, value: String): void
+ reloadFromFile(): void
+ getMaxBorrowDays(): Integer
+ getLateFeePerDay(): Money
+ getInstance(): ConfigurationManager
+ getSetting(key: String): String
+ setSetting(key: String, value: String): void
+ reloadFromFile(): void
+ getMaxBorrowDays(): Integer
+ getLateFeePerDay(): Money
Key Design Decisions:
First, Private constructor loads settings from
Second,
Third,
Fourth,
Testing Consideration: To make testable, add a method
First, Private constructor loads settings from
filePath into settings map during initialization.Second,
getSetting(key) provides type-agnostic access. Convenience methods like getMaxBorrowDays() parse and return typed values.Third,
reloadFromFile() re-reads the file and updates settings map. Critical: This method must be synchronized to prevent race conditions if called while other threads read settings. Alternatively, use copy-on-write strategy: create new map, populate it, then atomically swap the reference.Fourth,
lastModified timestamp tracks file changes. A background thread can periodically check if the file changed and auto-reload.Testing Consideration: To make testable, add a method
setInstanceForTesting(ConfigurationManager mock) that replaces the singleton with a test double, but mark it clearly as test-only and ensure production code never calls it.Question 4: When would you use multiple singletons versus one singleton managing multiple instances?
Multiple Singletons: Use when you have multiple independent logical resources. Example:
One Singleton Managing Multiple Instances: Use when you have one coordinator that manages multiple similar resources. Example:
Decision Rule: If the resources need centralized coordination or resource limits (total connections across all pools cannot exceed X), use one manager singleton. If resources are independent, use separate singletons or avoid singletons entirely through DI.
ConfigurationManager.getInstance(), Logger.getInstance(), CacheManager.getInstance(). Each manages its own domain and does not interact directly.One Singleton Managing Multiple Instances: Use when you have one coordinator that manages multiple similar resources. Example:
DatabaseConnectionPoolManager.getInstance() manages multiple connection pools (one per database). The manager is singleton, the pools are not.Decision Rule: If the resources need centralized coordination or resource limits (total connections across all pools cannot exceed X), use one manager singleton. If resources are independent, use separate singletons or avoid singletons entirely through DI.
Interview Tip: Explain that machine coding rounds test your ability to balance pattern theory with practical concerns like testability and thread safety. Always ask clarifying questions about concurrency requirements and testing strategy.
Question 5: What is the relationship between Singleton and the Open/Closed Principle (OCP)?
Tension: Singleton often violates OCP (Open/Closed Principle) because direct calls to
Resolution: Program to an interface, not the singleton directly. Define
Singleton.getInstance() tightly couple code to the concrete class. If you need to extend or modify singleton behavior, you must change existing code rather than extending through inheritance.Resolution: Program to an interface, not the singleton directly. Define
IConfigurationManager interface, have singleton implement it, and inject the interface. Now you can create alternative implementations (mock configurations, remote configurations) without modifying client code. The singleton still exists but is accessed through abstraction, preserving OCP.💡 Key Takeaways
✓Thread safety requires eager initialization, synchronized methods, or double-checked locking
✓Double-checked locking needs volatile variable and two null checks
✓Prevent cloning by throwing exception in clone() method
✓Prevent serialization attack by implementing readResolve() to return existing instance
✓ConfigurationManager singleton should support reloading with thread-safe updates
✓Use multiple singletons for independent resources; one manager for coordinated resources
✓Singleton can violate Open/Closed Principle; use interfaces to mitigate coupling
📌 Examples
1Thread-safe Logger using double-checked locking
2ConfigurationManager with readResolve() to handle deserialization
3DatabaseConnectionPoolManager singleton managing multiple connection pool instances