0
0
LLDsystem_design~7 mins

Thread safety in design in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
When multiple threads access shared data or resources without coordination, data corruption, race conditions, and unpredictable behavior occur. This leads to bugs that are hard to reproduce and fix, causing system crashes or incorrect results.
Solution
Thread safety ensures that shared data is accessed and modified in a controlled way so that only one thread can change it at a time or changes are done atomically. This is done by using locks, synchronization primitives, or designing immutable data structures to prevent conflicts and keep data consistent.
Architecture
Thread 1
Lock/Mutex
Thread 2
─────────────┘
Thread Safety
Thread Safety

This diagram shows multiple threads accessing a shared resource through a lock or mutex to ensure only one thread modifies the resource at a time, enforcing thread safety.

Trade-offs
✓ Pros
Prevents data corruption and race conditions by controlling concurrent access.
Makes system behavior predictable and easier to debug.
Enables safe parallel execution improving performance on multi-core systems.
✗ Cons
Introduces complexity in code design and debugging.
Can cause performance overhead due to locking and context switching.
Risk of deadlocks if locks are not managed carefully.
Use thread safety when multiple threads or processes access shared mutable data or resources concurrently, especially in systems with high parallelism or critical data consistency requirements.
Avoid complex thread safety mechanisms when the system is single-threaded or when shared data access is minimal and can be serialized without performance impact.
Real World Examples
Google
Google's search engine uses thread-safe caches to allow multiple threads to read and update cached data without corrupting the cache state.
Netflix
Netflix uses thread-safe data structures in their streaming service backend to handle millions of concurrent user requests without data races.
LinkedIn
LinkedIn employs thread safety in their messaging system to ensure message queues are updated correctly when accessed by multiple threads.
Code Example
The before code increments a shared counter without any synchronization, causing race conditions and incorrect results. The after code uses a lock to ensure only one thread increments the counter at a time, making the operation thread-safe and the final count correct.
LLD
### Before (no thread safety, race condition possible)
import threading

class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1

counter = Counter()

def worker():
    for _ in range(100000):
        counter.increment()

threads = [threading.Thread(target=worker) for _ in range(2)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(counter.value)  # Output may be less than 200000 due to race conditions


### After (with thread safety using Lock)
import threading

class Counter:
    def __init__(self):
        self.value = 0
        self.lock = threading.Lock()

    def increment(self):
        with self.lock:
            self.value += 1

counter = Counter()

def worker():
    for _ in range(100000):
        counter.increment()

threads = [threading.Thread(target=worker) for _ in range(2)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(counter.value)  # Output will reliably be 200000
OutputSuccess
Alternatives
Immutable Data Structures
Instead of locking, data is never changed after creation, so threads can read safely without synchronization.
Use when: Choose when the system can tolerate creating new copies of data rather than modifying in place, reducing locking overhead.
Actor Model
Encapsulates state inside actors that process messages sequentially, avoiding shared state and locks.
Use when: Choose when designing highly concurrent systems that benefit from message passing and isolated state.
Summary
Thread safety prevents data corruption and unpredictable behavior when multiple threads access shared resources.
It is achieved by controlling access using locks, synchronization, or immutable data.
Proper thread safety design balances correctness with performance and avoids deadlocks.