Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartSwiggyRazorpayPhonePeCRED

Singleton Pattern - Thread Safety, Double-Checked Locking & Lazy Init

Choose your preparation mode3 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Define Singleton class with _instance field

The Singleton class is defined with a private static field _instance initialized to null. This field will hold the single instance of the class.

💡 This setup is crucial because _instance is the core of the singleton pattern, storing the unique instance.
Line:class Singleton: _instance = null
💡 The singleton instance is stored as a class-level field, initially empty.
📊
Singleton Pattern - Thread Safety, Double-Checked Locking & Lazy Init - Watch the Algorithm Execute, Step by Step
Watching each step reveals how thread safety and lazy initialization are achieved, which is difficult to grasp by reading code alone.
Step 1/10
·Active fillAnswer cell
Encapsulation of the singleton instance in a private static field
Singleton
_instance: Singleton|null
Polymorphism: overriding __new__ to control instantiation
Singleton
_instance: Singleton|null
+__new__()
Lazy initialization check
Singleton
_instance: Singleton|null
+__new__()
Singleton instance creation and assignment
Singleton
_instance: Singleton
+__new__()
Consistent instance return
Singleton
_instance: Singleton
+__new__()
Multiple calls to singleton constructor
Singleton
_instance: Singleton
+__new__()
Double-checked locking simulation: no new instance created
Singleton
_instance: Singleton
+__new__()
Singleton instance reuse
Singleton
_instance: Singleton
+__new__()
Verification of singleton uniqueness
Singleton
_instance: Singleton
+__new__()
Singleton pattern with thread-safe lazy initialization
Singleton
_instance: Singleton
+__new__()

Key Takeaways

Singleton instance is created lazily and stored in a private static field.

This insight is hard to see from code alone because the lazy creation and storage happen inside __new__, which is not obvious without tracing.

Multiple threads calling getInstance() concurrently receive the same instance without creating duplicates.

Visualizing the check and return steps clarifies how thread safety is maintained, which is subtle in code.

Overriding __new__ (or using synchronized getInstance) controls instance creation and enforces singleton behavior.

Seeing the method activation and decision branches helps understand how polymorphism and synchronization enforce the pattern.

Practice

(1/5)
1. In the object-oriented design of a Snake and Ladder game, which component is primarily responsible for managing the state transitions of a player's position after a dice roll?
easy
A. The Dice class, since it generates the number that determines movement
B. The Player class, as it holds the current position and updates it directly
C. The Board class, because it contains the snakes and ladders and applies their effects
D. The GameController class, which orchestrates the game flow and updates player positions accordingly

Solution

  1. Step 1: Understand the role of Dice

    The Dice only generates a random number; it does not manage state transitions.
  2. Step 2: Consider Player class responsibilities

    Player holds position but should not decide how to update it considering snakes or ladders.
  3. Step 3: Analyze Board class role

    Board knows snakes and ladders but does not manage player state transitions directly.
  4. Step 4: Role of GameController

    GameController coordinates dice roll, queries Board for snakes/ladders, and updates Player position accordingly.
  5. Final Answer:

    Option D -> Option D
  6. Quick Check:

    GameController centralizes state transitions, ensuring separation of concerns.
Hint: GameController orchestrates state changes, not Dice or Player alone [OK]
Common Mistakes:
  • Thinking Dice manages player position
  • Assuming Player updates position without Board's input
  • Believing Board directly changes player state
2. Which of the following statements about the Adapter pattern is INCORRECT?
medium
A. Adapter changes the interface of an existing object to match what the client expects
B. Adapter can be implemented using inheritance or composition
C. Adapter adds new functionality to the adapted object without modifying it
D. Adapter is used to simplify a complex subsystem by providing a unified interface

Solution

  1. Step 1: Review Adapter intent

    Adapter converts incompatible interfaces to make them compatible.
  2. Step 2: Check each statement

    A is correct: Adapter changes interface. B is correct: Adapter can use inheritance or composition. C is correct: Adapter can add behavior without modifying original object. D is incorrect: Simplifying a complex subsystem is Facade's role, not Adapter's.
  3. Final Answer:

    Option D -> Option D
Hint: Adapter = interface converter; Facade = interface simplifier
Common Mistakes:
  • Confusing Adapter with Facade's simplification role
  • Thinking Adapter only uses inheritance
  • Assuming Adapter cannot add new behavior
3. Which of the following is a common trade-off or limitation when favoring composition over inheritance in object-oriented design?
medium
A. Composition always leads to more complex code and harder maintenance than inheritance
B. Composition can increase the number of objects and indirection, potentially impacting performance
C. Composition prevents code reuse since behaviors cannot be shared
D. Composition forces tight coupling between composed objects

Solution

  1. Step 1: Evaluate each option's validity

    Composition always leads to more complex code and harder maintenance than inheritance is false; composition often improves maintainability. Composition prevents code reuse since behaviors cannot be shared is false; composition promotes code reuse via behavior objects. Composition forces tight coupling between composed objects is false; composition reduces coupling by separating concerns.
  2. Step 2: Understand the trade-off

    Composition introduces more objects and delegation layers, which can add runtime overhead and complexity in object management.
  3. Step 3: Confirm correct trade-off

    Composition can increase the number of objects and indirection, potentially impacting performance correctly identifies the potential performance and complexity cost of increased indirection.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Composition trades off some runtime overhead for flexibility and maintainability.
Hint: Composition trades flexibility for some performance overhead, not complexity or coupling.
Common Mistakes:
  • Believing composition always simplifies code without cost
  • Thinking composition prevents code reuse
4. Which of the following statements about the Open/Closed Principle is INCORRECT?
medium
A. OCP means you should never modify existing code once it's written
B. OCP encourages designing modules that can be extended without changing their source code
C. Abstraction and polymorphism are key enablers of OCP
D. OCP helps reduce bugs by minimizing changes to tested code

Solution

  1. Step 1: Analyze statement A

    OCP does not forbid all modifications; it encourages minimizing changes to stable, tested code but allows modifications when necessary.
  2. Step 2: Validate other statements

    Statements B, C, and D correctly describe OCP's goals and mechanisms.
  3. Step 3: Why A is incorrect

    Absolute prohibition of modification is impractical; OCP is about minimizing and isolating changes.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    OCP is about minimizing, not forbidding, modifications.
Hint: OCP minimizes, but does not forbid, code changes [OK]
Common Mistakes:
  • Interpreting OCP as no code changes ever allowed
  • Ignoring the role of abstraction in OCP
  • Underestimating OCP's impact on bug reduction
5. Which of the following statements about method overriding is INCORRECT?
medium
A. Overriding methods must have the same method signature as the method in the superclass
B. A subclass can call the superclass's overridden method using a special keyword
C. Overriding enables runtime polymorphism through dynamic dispatch
D. Overriding is resolved at compile time by the compiler

Solution

  1. Step 1: Review overriding rules

    Overriding requires the same method signature and enables runtime polymorphism.
  2. Step 2: Analyze each statement

    Overriding methods must have the same method signature as the method in the superclass is correct; signatures must match. Overriding enables runtime polymorphism through dynamic dispatch is correct; overriding uses dynamic dispatch. A subclass can call the superclass's overridden method using a special keyword is correct; subclasses can call superclass methods (e.g., via super keyword).
  3. Step 3: Identify incorrect statement

    Overriding is resolved at compile time by the compiler is incorrect because overriding is resolved at runtime, not compile time.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Overriding is a runtime mechanism, not compile-time.
Hint: Overriding = runtime binding, not compile-time
Common Mistakes:
  • Believing overriding is compile-time resolved
  • Confusing signature requirements with overloading
  • Ignoring ability to call superclass methods