❓ Concurrency & Parallelism - Q&A¶
Pairs with Concurrency & Parallelism. Format and tag legend: Overview.
Q1. Concurrency vs parallelism? Concurrency is structuring a program so tasks make progress independently (can be interleaved on one core); parallelism is executing tasks at the same instant on multiple cores. Concurrency is about structure, parallelism about execution - you can have one without the other. Follow-ups: Give an example of concurrent-but-not-parallel. (async event loop on one core.)
Easy·MAANGQ2. When would you use threads vs async? Async (an event loop) shines for I/O-bound work with many idle waits - thousands of connections, minimal per-task memory. Threads suit shared-state concurrency and can use preemption; processes suit CPU-bound work. The classic bug is a blocking/CPU-heavy call stalling an event loop. Follow-ups: Why does one blocking call hurt an event loop? (cooperative - no preemption.)
Med·MAANGQ3. What's a race condition? When the result depends on the timing of interleaved operations on shared state. The canonical example is
count += 1, which is read-modify-write; two threads can both read the old value and lose an update. Follow-ups: How do you fix it? (lock, atomic op, or don't share.)Easy·MAANGQ4. Why isn't
count += 1atomic? It compiles to three steps - read, add, write. Another thread can interleave between them, so both read the same old value and one increment is lost. Atomicity means the whole operation is indivisible; you get it with a lock or a hardware atomic instruction. Follow-ups: What's a compare-and-swap (CAS)?Med·MAANGQ5. Mutex vs semaphore? A mutex allows exactly one holder and has ownership (only the locker unlocks). A semaphore is a counter permitting up to N concurrent holders - useful to cap access to a pool (e.g. ≤10 DB connections). A binary semaphore looks like a mutex but lacks ownership. Follow-ups: When would a semaphore be the right tool? (connection/resource pools.)
Med·MAANGQ6. What is a deadlock and how do you avoid it in application code? Threads wait on locks each other holds, forever. It needs all four Coffman conditions; the practical fix is a consistent global lock-acquisition order so a cycle can't form. Also: lock timeouts, or acquiring all locks at once. Follow-ups: Show a two-lock deadlock and its fix.
Med·AmazonQ7. Deadlock vs livelock vs starvation? Deadlock: threads are stuck waiting forever (no progress, no CPU). Livelock: threads keep changing state reacting to each other but still make no progress. Starvation: a thread never gets a resource because others keep winning - fixed with fairness/aging. Follow-ups: Give a real livelock example. (two threads both backing off and retrying in sync.)
Hard·Q8. What is a critical section? Code that must not be executed by more than one thread at once because it touches shared state. Keep it as small as possible - holding a lock longer than necessary serializes threads and kills throughput. Follow-ups: Why is a coarse lock bad for scalability?
Easy·MAANGQ9. Optimistic vs pessimistic locking? Pessimistic locks first, assuming conflict is likely (safe, blocks others, risks deadlock). Optimistic proceeds without locking and checks at commit that nothing changed (via a version number); if it did, retry. Optimistic wins when conflicts are rare. Follow-ups: How does this map to databases? (MVCC / version columns.)
Med·AmazonQ10. What does the Python GIL mean for concurrency? Only one thread executes Python bytecode at a time, so threads don't parallelize CPU-bound work. Use
multiprocessing(or native libs that release the GIL) for CPU-bound tasks; threads orasyncioare fine for I/O-bound work since the GIL is released during blocking I/O. Follow-ups: Why do threads still help for I/O in Python?Med·MAANGQ11. Explain Amdahl's law. Speedup from parallelism is capped by the serial fraction: if fraction s must run sequentially, max speedup is 1/s regardless of core count. So 10% serial work caps you at 10×. Lesson: parallelize the dominant parallel part and don't expect linear scaling. Follow-ups: What's the practical implication for adding cores? (diminishing returns.)
Hard·Q12. What is a thread pool and why use one? A fixed set of reusable worker threads pulling tasks from a queue. It avoids the cost of spawning a thread per task and bounds concurrency, which protects downstream resources (DB, CPU). Most web servers and executors use this model. Follow-ups: What happens if the pool is too small? Too large?
Med·MAANGQ13. Explain the producer-consumer pattern. Producers push work onto a bounded queue; consumers pull and process it. The queue decouples their rates and provides backpressure - producers block when it's full, so a fast producer can't overwhelm slow consumers. Follow-ups: What coordinates the handoff? (condition variable / blocking queue.)
Med·AmazonQ14. What's the difference between a lock and an atomic operation? A lock guards an arbitrary critical section but has acquire/release overhead and deadlock risk. An atomic operation is a single indivisible hardware instruction (increment, CAS) - faster and lock-free, but only for simple operations. Follow-ups: When would you prefer atomics? (counters, flags, lock-free structures.)
Hard·Q15. What is a read-write lock? It allows many concurrent readers OR one exclusive writer. Good for read-heavy shared data where reads vastly outnumber writes. The risk is writer starvation if readers keep arriving - needs a fairness policy. Follow-ups: When is a plain mutex better? (write-heavy or very short critical sections.)
Med·MAANGQ16. What is thread safety? Code is thread-safe if it behaves correctly when accessed by multiple threads concurrently, without external synchronization. Achieved via immutability, confinement (no sharing), or proper locking around shared mutable state. Follow-ups: Why are immutable objects inherently thread-safe? (no writes to race on.)
Med·MAANGQ17. What is a condition variable? A synchronization primitive that lets a thread wait until some predicate becomes true, releasing the associated lock while waiting and reacquiring it on wakeup. Used for producer-consumer handoff ("wait until queue non-empty"). Follow-ups: Why check the predicate in a loop? (spurious wakeups.)
Hard·Q18. What causes false sharing? Two threads modify independent variables that happen to sit on the same cache line, so the cache coherence protocol keeps invalidating each other's cache - killing performance despite no logical contention. Fix with padding/alignment. Follow-ups: How would you diagnose it? (profiler showing cache-line contention.)
Hard·Q19. At-least-once vs at-most-once vs exactly-once delivery? At-least-once may deliver duplicates (retry on uncertainty) - needs idempotent consumers. At-most-once may drop messages (no retry). Exactly-once is hard in distributed systems; usually approximated by at-least-once + idempotency/dedup. Follow-ups: How do you make a consumer idempotent? (dedup key, upsert.)
Hard·AmazonQ20. What is optimistic concurrency with a version number? Read a record with its version; on write, update only if the version still matches, then bump it. If another writer got there first the version mismatches and you retry. Avoids locks when conflicts are rare. Follow-ups: What's the failure mode under high contention? (retry storms.)
Med·StartupsQ21. How do you share state safely across processes? Since processes don't share memory, use IPC: shared memory (fast, needs synchronization), message passing/queues (safer, copies), or an external store (Redis/DB). Message passing is usually the cleaner default. Follow-ups: Why is "share nothing" a popular concurrency model? (no locks, no races.)
Med·MAANGQ22. Why is "just add more locks" a bad instinct? More locks increase deadlock risk, reduce concurrency (threads serialize), and add overhead. Prefer smaller critical sections, a consistent lock order, lock-free atomics, or eliminating sharing (immutability, confinement) over piling on locks. Follow-ups: What's lock contention and how do you measure it?
Med·MAANG
Related: Concurrency & Parallelism · Operating Systems Q&A · Python Language Q&A