1/21
Looks like no tags are added yet.
Threads and processes
A thread is an execution path within a process. Threads in the same process can share memory, which makes communication convenient but introduces synchronization problems. Separate processes generally have separate address spaces.
Concurrency vs. Parallelism
Concurrency is two lines of customers ordering from a single cashier (lines take turns ordering, overlapping progress); Parallelism is two lines of customers ordering from two cashiers (each line gets its own cashier, literally executing at the same time).
Concurrency issues stem from the fact that threads in the same process share memory.
Race condition
A race condition occurs when the result depends on the timing or interleaving of concurrent operations. Two threads read a counter with value 5, both add 1, and both write 6. The expected value of 7 is lost. This is a classic lost-update race.
Critical sections
A critical section is code that accesses shared state and may need protection.
Mutex
A mutex (mutual-exclusion lock) ensures that only one thread at a time can enter a protected critical section. Other threads trying to acquire that lock must wait until it becomes available.
Know when to lock, how to keep the critical section short, and how to avoid leaving a lock held after an error.
Semaphores
A semaphore maintains a count of available permits. A thread acquires a permit before proceeding and releases it afterward. This can limit how many threads access a resource simultaneously. They allow us to limit concurrent operations (e.g., before downloading, must get a permit), or limit aggregate consumption (e.g., each permit represents a unit of resource like 1 MB).
Use semaphores to limit concurrent operations, like when you can have at most 5 downloads or at most 10 API calls at a given time. This helps manage resource scarcity.
Structure of Interview
Functional Requirements
Non-functional requirements (Discuss latency, throughput, reliability, security, resource limits, and availability. For physical systems, clarify real-time deadlines, network connectivity, and what must keep working offline.)
Core entities and state (Identify the important domain objects, their relationships, and their state. Examples might include a sensor, measurement, vehicle, track, mission, or command, depending on the prompt.)
Interfaces/communication (Define how components communicate: important APIs, message types, inputs, outputs, and ownership. Don't automatically assume everything uses REST; internal communication might use queues, RPC, or direct in-process calls.)
End to end data flow (input to output)
High-Level Architecture (Draw the major components, their connections, and their responsibilities.)
Deep Dives (Discuss concurrency, performance, hashing, estimation, failure handling, security, and alternatives wherever relevant.)
Atomics
Atomics provide thread-safe operations on single variables without locks. Under the hood, they use CPU instructions like compare-and-swap (CAS) that complete in one uninterruptible step.
Consider tracking how many seats have been booked. With a regular integer, incrementing is unsafe because increment operations are actually three steps (read, add, write) that can interleave. With an atomic integer, the increment happens as one indivisible operation. This pattern is called optimistic concurrency. You optimistically assume no one else will interfere, do your work, and only retry if that assumption was wrong. Under low contention most CAS attempts succeed on the first try, making this faster than acquiring a lock.
Condition Variables
Condition variables let threads wait efficiently for a condition to become true. A thread acquires a lock, checks a condition, and if not satisfied, waits. This atomically releases the lock and puts the thread to sleep. When another thread signals, waiters wake up and re-check.
Blocking Queues
Blocking queues combine a queue with condition variables to provide thread-safe producer-consumer handoff. Producers call put() to add items; if full, they block. Consumers call take() to remove items; if empty, they block.
Correctness
Correctness is about preventing data corruption when multiple threads access shared state. Two threads both book the same seat. A counter that should be 1000 reads 847. A bank balance missing deposits. The danger isn't deadlock or performance, it's silently producing wrong results.
Coarse-Grained Locking
When a thread acquires a lock, every other thread trying to acquire that same lock has to wait until the first thread releases it. Coarse-grained locking means using one lock to guard all booking operations. Every thread that wants to check or modify seat availability waits for the same lock.
Read-Write Lock
Sometimes your workload is heavily skewed toward reads. A read-write lock has two modes: read (shared) and write (exclusive). Multiple threads can hold the read lock simultaneously since they're just reading and can't corrupt each other's view. But the write lock is exclusive. When a thread wants to write, it waits for all readers to finish, then blocks everyone else until the write completes.
Fine-Grained Locking
Fine-grained locking uses multiple locks, where each lock protects a smaller piece of state. Instead of one lock for the entire venue, you have one lock per seat. Threads only block each other when they're competing for the same resource. It solves the issues that coarse-grained locking has at scale. To prevent deadlock, always acquire locks in a consistent order.
Coordination
Coordination is about threads communicating and handing off work. One thread produces tasks, another consumes them. A service sends a request, another service processes it.
Shared State Coordination
How do threads communicate and sequence their work? They need to signal each other ("work is ready"), wait efficiently without burning CPU, and handle the case where one side is faster than the other.
The most common approach to coordination uses shared data structures. Threads communicate by reading and writing to the same memory. A producer adds an item to a queue, a consumer removes it. The queue is the shared state, and synchronization primitives like locks and condition variables ensure threads don't step on each other.
Condition Variables allow workers to actually sleep when there's nothing to do, then wake up the instant work arrives. Blocking queues implement this for you.
Message Passing Coordination
Instead of multiple threads accessing shared data structures, what if each thread had its own private state and communicated only by sending messages?
This is the actor model. An actor is an independent unit of computation with three properties: it has a mailbox (a queue of incoming messages), it processes messages one at a time, and it can send messages to other actors.
Resource Pooling
Resource pooling is a solution to resource scarcity - specifically, when connection pools are scarce. When a thread needs a connection, it pulls from the blocking queue. If the queue is empty because all connections are checked out, the thread blocks. When another thread finishes, it returns the connection to the queue, waking one waiting thread. Semaphores cannot contain and dispense the actual connection objects, but a queue can. This is useful when you have objects that are expensive to create, but can be reused.
Deadlock, starvation, and livelock
Deadlock: Two threads wait forever for locks held by each other.
Starvation: A thread waits indefinitely because other threads keep getting the resource.
Livelock: Threads keep reacting to one another, but neither makes useful progress.
Backpressure, bounded queues, and overload
Solutions for when producers permanently outpace consumers.
A bounded queue prevents unlimited memory growth, but then you need a policy:
Block producers.
Reject new work.
Drop or coalesce obsolete updates when the domain permits it.
Add capacity or scale consumers if the bottleneck can be addressed that way.
time units
1 ns
Nanosecond
One-billionth of a second.
10^-9 seconds
1 μs
Microsecond
One-millionth of a second.
10^-6 seconds = 1,000 ns
1 ms
Millisecond
One-thousandth of a second.
10^-3 seconds = 1,000 μs
1 s
Second
One second.
1,000 ms