Bird
Raised Fist0
LLDsystem_design~10 mins

Thread safety in design in LLD - Interactive Code Practice

Choose your learning style10 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
Practice - 5 Tasks
Answer the questions below
1fill in blank
easy

Complete the code to declare a thread-safe counter using a lock.

LLD
class Counter:
    def __init__(self):
        self.value = 0
        self.lock = [1]

    def increment(self):
        with self.lock:
            self.value += 1
Drag options to blanks, or click blank then click option'
Athreading.Event()
Bthreading.Condition()
Cthreading.Thread()
Dthreading.Lock()
Attempts:
3 left
💡 Hint
Common Mistakes
Using Thread instead of Lock causes errors because Thread is for creating threads, not synchronization.
Using Event or Condition without proper locking does not guarantee exclusive access.
2fill in blank
medium

Complete the code to safely read a shared variable with a read-write lock.

LLD
class SharedData:
    def __init__(self):
        self.data = 0
        self.rw_lock = [1]

    def read(self):
        self.rw_lock.acquire_read()
        value = self.data
        self.rw_lock.release_read()
        return value
Drag options to blanks, or click blank then click option'
Athreading.Semaphore()
BReadWriteLock()
Cthreading.Lock()
Dthreading.RLock()
Attempts:
3 left
💡 Hint
Common Mistakes
Using a simple Lock blocks all threads, reducing concurrency.
RLock allows reentrant locking but does not differentiate read/write access.
3fill in blank
hard

Fix the error in the code to avoid race conditions when updating a shared list.

LLD
shared_list = []

lock = threading.Lock()

def add_item(item):
    [1]  # Fix: ensure thread safety
    shared_list.append(item)
    lock.release()
Drag options to blanks, or click blank then click option'
Alock.acquire()
Block.release()
Clock.wait()
Dlock.notify()
Attempts:
3 left
💡 Hint
Common Mistakes
Calling release before acquire causes runtime errors.
Using wait or notify without proper locking does not prevent race conditions.
4fill in blank
hard

Fill both blanks to implement a thread-safe singleton pattern.

LLD
class Singleton:
    _instance = None
    _lock = [1]

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = [2]  # Create instance
        return cls._instance
Drag options to blanks, or click blank then click option'
Athreading.Lock()
Bthreading.Thread()
CSingleton()
Dobject()
Attempts:
3 left
💡 Hint
Common Mistakes
Using Thread instead of Lock causes synchronization issues.
Creating the instance with object() does not call the Singleton constructor.
5fill in blank
hard

Fill all three blanks to implement a thread-safe producer-consumer queue.

LLD
import queue
import threading

class ProducerConsumer:
    def __init__(self):
        self.q = queue.Queue()
        self.lock = [1]
        self.not_empty = threading.Condition(self.lock)

    def produce(self, item):
        with self.lock:
            self.q.put(item)
            [2]  # Notify consumers

    def consume(self):
        with self.lock:
            while self.q.empty():
                [3]  # Wait for items
            return self.q.get()
Drag options to blanks, or click blank then click option'
Athreading.Lock()
Bself.not_empty.notify()
Cself.not_empty.wait()
Dthreading.Event()
Attempts:
3 left
💡 Hint
Common Mistakes
Not using a lock causes race conditions.
Calling notify without holding the lock causes errors.
Waiting without a loop can cause missed signals.

Practice

(1/5)
1. What does thread safety in system design primarily ensure?
easy
A. Multiple threads can access shared data without causing errors
B. The system runs faster by using more threads
C. Only one thread runs at a time in the entire system
D. Threads do not use any shared resources

Solution

  1. Step 1: Understand thread safety concept

    Thread safety means multiple threads can work with shared data without causing conflicts or errors.
  2. Step 2: Analyze options

    Multiple threads can access shared data without causing errors correctly states this. Options B, C, and D misunderstand thread safety or describe unrelated concepts.
  3. Final Answer:

    Multiple threads can access shared data without causing errors -> Option A
  4. Quick Check:

    Thread safety = safe shared data access [OK]
Hint: Thread safety means safe shared data access [OK]
Common Mistakes:
  • Confusing thread safety with performance
  • Thinking only one thread runs at a time
  • Assuming no shared data is used
2. Which of the following is the correct way to declare a lock object in a typical low-level design for thread safety?
easy
A. lock = synchronized()
B. lock = new Lock()
C. lock = create_lock()
D. lock = Lock()

Solution

  1. Step 1: Identify common lock declaration syntax

    In many low-level designs, a lock is created by calling a constructor like Lock().
  2. Step 2: Compare options

    lock = Lock() uses lock = Lock(), which is typical. lock = new Lock() uses 'new' which is not common in low-level design languages. lock = create_lock() and D use incorrect or non-standard functions.
  3. Final Answer:

    lock = Lock() -> Option D
  4. Quick Check:

    Lock creation = Lock() [OK]
Hint: Lock objects are usually created by calling Lock() [OK]
Common Mistakes:
  • Using 'new' keyword incorrectly
  • Assuming lock creation uses special functions
  • Confusing lock with synchronization keyword
3. Consider this pseudocode for a shared counter increment:
lock.acquire()
counter = counter + 1
lock.release()
print(counter)
If two threads run this code simultaneously starting with counter = 0, what is the possible output?
medium
A. 0
B. 3
C. 2
D. Any number greater than 2

Solution

  1. Step 1: Understand lock usage in code

    The lock ensures only one thread increments the counter at a time, preventing race conditions.
  2. Step 2: Calculate final counter value

    Two threads each increment once, so counter goes from 0 to 2 safely.
  3. Final Answer:

    2 -> Option C
  4. Quick Check:

    Lock ensures increments are safe, so counter = 2 [OK]
Hint: Locks prevent lost updates, so increments add up [OK]
Common Mistakes:
  • Ignoring lock and assuming race condition
  • Thinking output can be 0 or 1 due to concurrency
  • Assuming counter can exceed 2 without loops
4. In this code snippet, what is the main thread safety issue?
lock.acquire()
shared_list.append(1)
# Missing lock.release()
medium
A. No issue, code is safe
B. Deadlock due to missing lock release
C. Syntax error in lock usage
D. Race condition on shared_list

Solution

  1. Step 1: Analyze lock usage

    The code acquires a lock but never releases it, causing other threads to wait forever.
  2. Step 2: Identify consequence

    This causes a deadlock, where threads block indefinitely waiting for the lock.
  3. Final Answer:

    Deadlock due to missing lock release -> Option B
  4. Quick Check:

    Missing release = deadlock [OK]
Hint: Always release locks to avoid deadlocks [OK]
Common Mistakes:
  • Thinking race condition occurs despite lock
  • Assuming syntax error without checking code
  • Believing code is safe without release
5. You design a system where multiple threads update a shared cache. To improve performance, you want to minimize locking time. Which design approach best balances thread safety and performance?
hard
A. Use fine-grained locks for each cache entry
B. Avoid locks and allow unsynchronized updates
C. Use a single global lock for all cache updates
D. Lock the entire cache for every read and write

Solution

  1. Step 1: Understand locking strategies

    A single global lock (Use a single global lock for all cache updates) causes contention and slows performance. No locks (Avoid locks and allow unsynchronized updates) risks data corruption. Locking entire cache for reads and writes (Lock the entire cache for every read and write) is too heavy.
  2. Step 2: Choose fine-grained locks

    Fine-grained locks (Use fine-grained locks for each cache entry) lock only parts of the cache, reducing waiting time and keeping thread safety.
  3. Final Answer:

    Use fine-grained locks for each cache entry -> Option A
  4. Quick Check:

    Fine-grained locks = safety + speed [OK]
Hint: Fine-grained locks reduce wait and keep safety [OK]
Common Mistakes:
  • Using one big lock causing slowdowns
  • Skipping locks causing data errors
  • Locking too much causing bottlenecks