Bird
Raised Fist0

Traffic Light System - State Machine, Sensor Input, Emergency Override

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Traffic Light System - State Machine, Sensor Input, Emergency Override
40 min ยท medium
medium

Design a traffic light system controlling multiple intersections with sensors detecting vehicle presence and an emergency override to switch lights immediately for emergency vehicles.

  • The system manages 4 intersections, each with its own traffic lights.
  • Each traffic light cycles through states: GREEN, YELLOW, RED.
  • Vehicle sensors detect waiting vehicles to optimize light changes.
  • An emergency override can preempt normal cycles to allow emergency vehicles to pass.
  • State transitions must be thread-safe to handle concurrent sensor inputs and overrides.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Traffic Light States Using Enum
Design an enum to represent the traffic light states and explain why enum is preferred over String constants.
๐Ÿ’ก Hint
Use Java enum to ensure type safety and exhaustive switch-case handling.
Use Java enum TrafficLightState {GREEN, YELLOW, RED} - enums provide compile-time safety and prevent runtime typo bugs unlike Strings.
Mid: Names enum usage for states. Senior: Explains how enum enables exhaustive switch and prevents invalid states at compile time.
โš ๏ธCommon Wrong Answer
Using String constants for states leads to runtime bugs due to typos and no compile-time checks.
Step 2
Model Traffic Light as Abstract Class with State
Design a TrafficLight abstract class holding current state and transition logic. Why abstract class over interface?
๐Ÿ’ก Hint
Abstract class can hold instance fields and provide shared behavior; interfaces cannot hold state in Java.
abstract class TrafficLight with volatile TrafficLightState currentState and synchronized state transition methods - abstract class needed for shared mutable state.
Mid: Identifies abstract class for shared state and behavior. Senior: Explains volatile ensures visibility of currentState across threads and synchronized methods prevent race conditions during state changes.
โš ๏ธCommon Wrong Answer
Using interface for TrafficLight state leads to inability to hold currentState field, forcing external state management and complicating concurrency.
Step 3
Implement Sensor Input Handling with Thread Safety
Design how vehicle sensors notify TrafficLight to change state safely under concurrency.
๐Ÿ’ก Hint
Use synchronized methods or locks to atomically check sensor input and update state.
public synchronized void onVehicleDetected() in TrafficLight to atomically check currentState and trigger state change - prevents race conditions from concurrent sensor threads.
Mid: Uses synchronized method for atomicity. Senior: Traces thread interleaving where without synchronization two threads could simultaneously trigger conflicting state changes.
โš ๏ธCommon Wrong Answer
Not synchronizing sensor input handling causes race conditions where multiple threads read same state and cause inconsistent transitions.
Step 4
Design Emergency Override with Atomic State Preemption
How to implement emergency override that preempts normal cycles immediately and safely?
๐Ÿ’ก Hint
Use synchronized method to set state to GREEN for emergency direction and volatile flag to indicate override active.
volatile boolean emergencyActive flag and public synchronized void activateEmergencyOverride() method to atomically set override and change state - ensures visibility and atomic preemption.
Mid: Uses synchronized method and volatile flag. Senior: Explains how volatile guarantees visibility of emergencyActive flag changes across threads and synchronized prevents interleaved state changes during override activation.
โš ๏ธCommon Wrong Answer
Without volatile, emergencyActive flag updates may not be visible to other threads, causing override to fail or delayed.
Step 5
Manage Multiple Intersections with Composition and Lifecycle
How to model multiple intersections each with their own traffic lights and sensors? Explain composition vs aggregation here.
๐Ÿ’ก Hint
Use composition: Intersection class contains TrafficLight instances; lifecycle of lights tied to intersection.
Intersection class composition of TrafficLight objects - TrafficLight lifecycle bound to Intersection, enforcing strong ownership and encapsulation.
Mid: Identifies composition relationship. Senior: Explains lifecycle ownership means TrafficLight cannot exist independently of Intersection, enforcing encapsulation and easier resource management.
โš ๏ธCommon Wrong Answer
Using aggregation or association leads to loose lifecycle coupling causing potential resource leaks or inconsistent state if TrafficLight outlives Intersection.
๐Ÿ”Œ
API Design Challenge
Design an API for external systems to interact with the traffic light system, including sensor input notifications and emergency override activation.
  • Consider thread-safe methods for sensor input and override activation.
  • Design clear method signatures for state queries and control commands.
  • Ensure the API supports multiple intersections and concurrent requests.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Use enum for traffic light states to prevent runtime bugs
  • abstract class TrafficLight with volatile state and synchronized transitions
  • Synchronized sensor input handling to prevent race conditions
  • volatile flag and synchronized method for emergency override
  • Composition relationship between Intersection and TrafficLight with lifecycle ownership
Good to Have - 30%
  • State machine pattern for traffic light behavior
  • Exhaustive switch-case on enum states
  • Clear separation of concerns between sensor input and state transitions
  • Thread-safe notification mechanism for sensor events
Bonus - 10%
  • Use of java.util.concurrent utilities for advanced concurrency
  • Extensible design allowing new states or override types without modifying existing classes
Using String constants for states leads to typo bugs and no compile-time checks, causing runtime errors and debugging difficulty.
Using interface to hold mutable state causes design complexity since interfaces cannot hold fields, forcing external state management and risking concurrency issues.
Not synchronizing state transitions causes race conditions and inconsistent traffic light states, leading to unsafe traffic behavior.
Not using volatile for shared flags causes visibility issues and stale reads across threads, resulting in delayed or missed state updates.
Using aggregation instead of composition for TrafficLight lifecycle leads to resource leaks and inconsistent ownership, as TrafficLight may outlive Intersection causing stale references.
โœ…
Model Answer
โ–ผ Reveal

The traffic light system uses a Java enum to define states ensuring type safety and exhaustive handling. An abstract TrafficLight class holds a volatile currentState field with synchronized methods to manage state transitions safely under concurrency. Vehicle sensors notify the TrafficLight via synchronized methods to prevent race conditions. Emergency override is implemented using a volatile boolean flag and synchronized method to atomically preempt normal cycles. Multiple intersections are modeled via composition, with Intersection class owning TrafficLight instances, ensuring lifecycle management and encapsulation.

  • Use Java enum for traffic light states to prevent runtime bugs and enable exhaustive switch-case handling.
  • Implement TrafficLight as abstract class with volatile state and synchronized methods to ensure thread-safe mutable state management.
  • Synchronize sensor input handling methods to prevent race conditions from concurrent threads.
  • Use volatile flag for emergency override status and synchronized method to atomically activate override ensuring visibility and atomicity.
  • Model multiple intersections using composition to enforce strong lifecycle ownership and encapsulation of TrafficLight instances.
  • Use Java enum for TrafficLightState to ensure type safety and exhaustive switch-case handling.
  • abstract class TrafficLight holds volatile currentState field and synchronized methods for state transitions to ensure thread-safe mutable state.
  • Synchronized onVehicleDetected() method to atomically handle sensor input and trigger state changes preventing race conditions.
  • volatile boolean emergencyActive flag combined with synchronized activateEmergencyOverride() method to guarantee visibility and atomic override activation.
  • Intersection class composes multiple TrafficLight instances, enforcing lifecycle ownership and encapsulation.
  • State machine pattern implemented via enum and synchronized state transition methods to model traffic light behavior.
  • Avoid using String constants for states or flags to prevent runtime bugs and poor IDE support.
  • Use volatile keyword to ensure visibility of shared mutable state across threads in Java memory model.
  • Synchronized methods provide atomicity and mutual exclusion for state changes preventing interleaved inconsistent updates.
  • Composition relationship enforces strong lifecycle ownership, simplifying resource management and preventing dangling references.

Practice

(1/5)
1. In a Wallet System design, the Transaction class has a private constructor and a static synchronized getInstance() method that returns the single instance. Which design pattern is applied here and why is this the correct use case?
easy
A. Factory Method pattern, because it controls object creation with a static method.
B. Singleton pattern, ensuring only one Transaction instance exists to maintain consistency.
C. Builder pattern, to construct complex Transaction objects step-by-step.
D. Prototype pattern, to clone existing Transaction instances efficiently.

Solution

  1. Step 1: Identify the pattern from constructor and method signatures

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single instance is created and accessed thread-safely.
  2. Step 2: Why Singleton fits this Wallet Transaction scenario

    Singleton is appropriate when exactly one instance is needed to coordinate actions or maintain global state, such as a ledger manager or transaction coordinator. Factory Method and Builder patterns do not enforce single instance, and Prototype is for cloning, not restricting instances.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Private constructor + synchronized static getInstance() -> Singleton pattern [check]
Hint: Private constructor + static synchronized method -> Singleton [check]
Common Mistakes:
2. In a multi-threaded parking lot system using Abstract Factory to create vehicles and spots, if parkVehicle() is not synchronized, which exact thread interleaving causes double-booking of the same spot?
medium
A. Thread 1 parks vehicle, Thread 2 parks vehicle after Thread 1 finishes.
B. Thread 1 checks spot availability -> true, context switch; Thread 2 checks spot availability -> true; both park simultaneously.
C. Thread 1 parks vehicle, Thread 2 waits for Thread 1 to release lock before parking.
D. Thread 1 and Thread 2 both fail to find any available spots and abort.

Solution

  1. Step 1: Identify race condition in parkVehicle()

    Without synchronization, two threads can both check spot availability before either marks it occupied.
  2. Step 2: Trace exact interleaving

    Thread 1 calls isAvailable() -> true, context switch before marking occupied; Thread 2 calls isAvailable() -> true; both proceed to park, causing double-booking.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap without synchronization causes double-booking [check]
Hint: Check-then-act gap -> both see available, both park [check]
Common Mistakes:
3. In the Food Ordering System, the KitchenQueue class has a parkOrder() method that checks if a slot is available before assigning an order. Without synchronization on parkOrder(), which exact thread interleaving causes double-booking of the same slot?
medium
A. Thread 1 and Thread 2 both call parkOrder() simultaneously and the JVM throws an exception.
B. Thread 1 checks slot availability -> true, context switch before assignment; Thread 2 checks availability -> true, both assign same slot.
C. Thread 1 assigns slot, then Thread 2 waits for Thread 1 to finish before assigning next slot.
D. Thread 1 and Thread 2 both wait on a lock, causing deadlock and no assignment.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads slot as available but is preempted before assignment; Thread 2 also reads available and proceeds.
  2. Step 2: Both threads assign the same slot leading to double-booking

    Because the availability check and assignment are not atomic, both threads assign the same slot concurrently.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap without sync -> double assignment [check]
Hint: Check availability -> context switch -> both assign same slot [check]
Common Mistakes:
4. In LinkedIn's ConnectionManager, the method addConnection(User a, User b) checks if users are already connected before adding. Without synchronizing this method, what exact thread interleaving causes a double connection to be created?
medium
A. Thread 1 checks isConnected(a,b) -> false, context switch; Thread 2 checks isConnected(a,b) -> false; both add connections.
B. Thread 1 and Thread 2 both call addConnection simultaneously, but one fails due to a database constraint.
C. Thread 1 adds connection, then Thread 2 removes it before Thread 1 commits.
D. Thread 1 and Thread 2 both read connection list after addition, causing stale cache.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, both threads can see no existing connection and proceed to add, causing duplicates.
  2. Step 2: Trace exact thread interleaving

    Thread 1 calls isConnected(a,b) and gets false, then context switches before adding. Thread 2 also calls isConnected(a,b) and gets false, then both add connections concurrently.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Check-then-act gap causes double connection addition [check]
Hint: Check-then-act without sync -> double add race [check]
Common Mistakes:
5. You need to add support for ElectricCar that requires ChargingSpot compatibility in the current parking system. What is the minimum change to the design to support this without modifying existing classes?
hard
A. Modify Vehicle class to add a boolean needsCharging and update parking logic to check this flag.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly.
C. Create a new ElectricParkingLot subclass that handles electric cars and charging spots separately.
D. Add a ChargingSpot field to Vehicle and modify all parking methods to handle it.

Solution

  1. Step 1: Identify extensibility requirements

    ElectricCar and ChargingSpot are new types requiring compatibility mapping without changing existing classes.
  2. Step 2: Apply minimal invasive change

    Adding new enum values and subclass plus updating compatibility map extends functionality without modifying existing class code, preserving OCP.
  3. Step 3: Avoid violating OCP

    Options B, C, and D modify existing classes or create parallel hierarchies, increasing complexity and violating OCP.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Extend enums and map, subclass ElectricCar -> minimal change [check]
Hint: Extend enums and map, subclass new type -> minimal 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