Bird
Raised Fist0

Singleton Pattern - Thread-Safe Implementation and When to Avoid It

LLDeasyInterview freq: high
Choose your preparation mode4 modes available
▶
Class Trace
Step 1/4
Singleton

Singleton class uses a private volatile static instance field and a private constructor.

🎯 Tests whether candidate knows volatile ensures visibility and prevents reordering so instance is fully constructed before reference is published.
public 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;
    }
}
SingletonManager composition

SingletonManager composes Singleton instance, owning its lifecycle and providing controlled access.

🎯 Tests understanding of composition relationship where SingletonManager lifecycle controls Singleton instance lifecycle.
public class SingletonManager {
    private final Singleton singleton = Singleton.getInstance();
    public Singleton getSingleton() {
        return singleton;
    }
}
SingletonAvoidanceAdvisor association

SingletonAvoidanceAdvisor is associated with SingletonManager but has independent lifecycle and advises when to avoid Singleton.

🎯 Tests candidate's knowledge of association meaning independent lifecycle and separation of concerns for advising pattern usage.
public class SingletonAvoidanceAdvisor {
    public boolean shouldAvoidSingleton() {
        // logic to determine if singleton pattern is appropriate
        return false;
    }
}
ClientThread association

ClientThread associates with SingletonManager to access Singleton instance concurrently.

🎯 Tests understanding of thread-safe access to Singleton via synchronized getInstance and how ClientThread uses it safely.
public class ClientThread extends Thread {
    private SingletonManager manager;
    public ClientThread(SingletonManager manager) {
        this.manager = manager;
    }
    public void run() {
        Singleton singleton = manager.getSingleton();
        // use singleton instance
    }
}
📐
Class Diagram
Class Diagram
       +------------------+
       |  <<Singleton>>    |
       |   Singleton       |
       +------------------+
       | - volatile inst.  |
       | - private ctor    |
       +------------------+
       | + getInstance():S |
       |       [sync]      |
       +------------------+
              │
              │ composition
              ▼
       +------------------+
       | SingletonManager |
       +------------------+
       | - singleton: S   |
       +------------------+
       | + getSingleton() |
       +------------------+
              │
              │ association
              ▼
       +-----------------------+
       | SingletonAvoidAdvisor |
       +-----------------------+
       | + shouldAvoidSingleton()|
       +-----------------------+
              │
              │ association
              ▼
       +--------------+
       | ClientThread |
       +--------------+
       | + run(): void|
       +--------------+

This diagram illustrates the thread-safe Singleton pattern using volatile and double-checked locking in the Singleton class to ensure safe lazy initialization. SingletonManager composes the Singleton instance, enforcing strong lifecycle ownership and encapsulation. SingletonAvoidanceAdvisor is associated with SingletonManager, representing an independent lifecycle and advising when to avoid Singleton usage, demonstrating separation of concerns. ClientThread associates with SingletonManager to safely access the Singleton instance concurrently, highlighting the importance of synchronized access and volatile for visibility. These design decisions enforce SOLID principles such as Single Responsibility and Open/Closed, while Java-specific constructs like volatile and synchronized ensure thread safety.

Primary Focus

✓ Singleton volatile usage + Double-Checked Locking + Composition vs Association lifecycle

Practice

(1/5)
1. In designing a TokenBucketRateLimiter class, should the RateLimiter be an abstract class or an interface? Consider that the rate limiter maintains internal token state and exposes tryConsume() behavior.
easy
A. Abstract class, because it can encapsulate shared token state and partial implementation
B. Interface, because it only defines behavior and state is managed externally
C. Interface, because Java interfaces can have private fields for token state
D. Abstract class, because interfaces cannot define any methods

Solution

  1. Step 1: Analyze state and behavior requirements

    RateLimiter needs to maintain internal token count state and provide tryConsume() behavior.
  2. Step 2: Abstract class vs interface for stateful behavior

    Abstract class can hold shared mutable state and provide partial implementation; interfaces cannot hold instance state.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class supports shared token state and behavior [check]
Hint: Stateful base class -> abstract class, not interface [check]
Common Mistakes:
2. In the ESPNcricinfo system, the Match class has a private constructor and a static synchronized getInstance() method to ensure only one instance per match ID exists. Which design pattern is applied here and why is this the correct use case?
easy
A. Singleton Pattern, because it ensures a single instance per match ID with thread safety.
B. Factory Pattern, because it creates objects based on input parameters.
C. Builder Pattern, because it separates construction and representation of Match objects.
D. Prototype Pattern, because it clones existing Match instances to avoid new creation.

Solution

  1. Step 1: Identify the pattern from constructor and getInstance()

    The private constructor prevents direct instantiation, and the static synchronized getInstance() ensures controlled, thread-safe instance creation.
  2. Step 2: Why Singleton fits this use case

    Singleton ensures only one Match instance per match ID exists, avoiding inconsistent state across threads.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Private constructor + synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + synchronized getInstance() -> Singleton [check]
Common Mistakes:
3. You want to add a WeekendBonusRule that gives players extra moves on weekends without modifying existing classes. Which SOLID principle guides the minimal change, and what is the correct approach?
medium
A. Single Responsibility Principle: add weekend logic inside the Player class.
B. Open/Closed Principle: create a new class implementing a MoveRule interface and inject it into the game engine.
C. Liskov Substitution Principle: subclass Player to WeekendPlayer with bonus logic.
D. Dependency Inversion Principle: make Player depend on concrete WeekendBonusRule.

Solution

  1. Step 1: Identify SOLID principle for extension without modification

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Evaluate approaches

    Subclassing Player to WeekendPlayer allows extension without modifying Player; injecting new MoveRule class also respects OCP but requires interface design.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Subclassing extends behavior without modifying base class [check]
Hint: Extend via subclass or interface, don't modify existing classes [check]
Common Mistakes:
4. If you want to add a new eviction policy (e.g., LFU) alongside LRU in the current cache design, which SOLID principle guides the minimal change, and what is the correct approach?
medium
A. Single Responsibility Principle: Modify the existing LRUCache class to handle both policies with flags.
B. Dependency Inversion Principle: Make the cache depend on concrete eviction classes directly to simplify design.
C. Open/Closed Principle: Create a new EvictionPolicy interface and implement LRU and LFU classes without changing existing cache code.
D. Liskov Substitution Principle: Replace the HashMap with a TreeMap to support ordering for LFU.

Solution

  1. Step 1: Identify the SOLID principle for extensibility

    Open/Closed Principle (OCP) states that software entities should be open for extension but closed for modification.
  2. Step 2: Analyze the approach

    Implementing an EvictionPolicy interface and injecting it into the cache decouples cache from eviction logic, following Dependency Inversion Principle (DIP), which is key here.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Cache depends on abstraction, not concrete eviction -> DIP [check]
Hint: Depend on abstractions, not concretes [check]
Common Mistakes:
5. ElectricCar requires ChargingSpots in the parking system using Prototype pattern. What is the minimum change to support this without modifying existing classes?
hard
A. Modify Vehicle base class to include charging logic and add ChargingSpot as a subclass of Spot
B. Add charging logic inside Vehicle clone() and modify SpotCompatibility map inside existing classes
C. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally
D. Create a god-class that handles both ElectricCar and ChargingSpot logic to avoid modifying existing classes

Solution

  1. Step 1: Identify extensibility requirements

    Adding ElectricCar and ChargingSpot must avoid modifying existing classes to respect OCP.
  2. Step 2: Evaluate options for minimal invasive change

    Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally adds new enum values and subclasses externally, updating compatibility map without changing existing code. Options B and C modify base classes, violating OCP. Create a god-class that handles both ElectricCar and ChargingSpot logic to avoid modifying existing classes creates god-class, violating SRP and maintainability.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    New enums + subclass + external map update -> minimal change, OCP respected [check]
Hint: New enums + subclass + external map -> minimal invasive change [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard