Concurrency
Doing more than one thing at a time and still being correct: threads and processes, locks and other primitives, the races and deadlocks they exist to prevent, memory models, async models, and parallel work distribution. Interviews lean hard on this area because concurrency bugs are the ones that survive testing.
part ofComputer science fundamentalsoverview, primer and where to startread it →on this pageshowhide
explore
- Threads and Processes24 questions
- Address-Space Isolation and IPC4 questions
- Thread Lifecycle5 questions
- Scheduling and Context Switching5 questions
- Kernel vs User Threading5 questions
- Thread-Local Context Propagation5 questions
- Synchronization Primitives30 questions
- Mutexes and Locks5 questions
- Semaphores5 questions
- Monitors and Condition Variables5 questions
- Read-Write and Optimistic Locks5 questions
- Spinlocks and Blocking Trade-offs5 questions
- Barriers and Latches5 questions
- Race Conditions and Deadlocks23 questions
- Data Races and Atomicity5 questions
- Deadlock Conditions and Detection4 questions
- Deadlock Prevention and Avoidance5 questions
- Livelock and Starvation5 questions
- Priority Inversion4 questions
- Memory Models and Atomics29 questions
- Visibility and Happens-Before4 questions
- Reordering and Fences6 questions
- Coherence and False Sharing4 questions
- Compare-and-Swap Primitives5 questions
- ABA and Safe Reclamation5 questions
- Lock-Free and Wait-Free Algorithms5 questions
- Async and Event-Driven Models30 questions
- Futures and Promises5 questions
- Coroutines and Cooperative Suspension5 questions
- Event Loops5 questions
- Reactor vs Proactor I/O6 questions
- Reactive Streams and Backpressure4 questions
- Mixing Blocking and Non-Blocking5 questions
- Parallelism and Work Distribution30 questions
- Data vs Task Decomposition6 questions
- Fork-Join and Divide-and-Conquer5 questions
- Work-Stealing Schedulers4 questions
- Speedup and Scalability Laws5 questions
- Pipelined Execution5 questions
- Map-Reduce and Parallel Reduction5 questions
- Thread Pools and Executors22 questions
- Choosing Worker Counts4 questions
- Work Queues and Rejection5 questions
- Graceful Shutdown and Draining4 questions
- Scheduled and Periodic Execution5 questions
- Saturation and Self-Deadlock4 questions
- Structured Concurrency19 questions
- Scopes and Task Lifetimes4 questions
- Cooperative Cancellation5 questions
- Failure Propagation and Supervision5 questions
- Timeouts and Deadline Propagation5 questions
- Message Passing and Isolation21 questions
- Producer-Consumer and Bounded Buffers5 questions
- Channels and CSP6 questions
- The Actor Model5 questions
- Immutability and Confinement5 questions
- Verifying Concurrent Code20 questions
- Deterministic Async Testing5 questions
- Race and Deadlock Detectors5 questions
- Model Checking and Fuzzing5 questions
- Production Thread Diagnostics5 questions
- Android Developerroleanchors this topic
- Computer Scienceskillanchors this topic
- iOS Developerroleanchors this topic
- AI & Data Scientistrole
- Backend Developerrole
- Blockchain Developerrole
- Data Analystrole
- Data Engineerrole
- Forward Deployed Engineerrole
- Full Stack Developerrole
- Game Developerrole
- Java Backend Developerrole
- Kotlin Backend Developerrole
- Machine Learning Engineerrole
- Server-Side Game Developerrole
- Software Architectrole
questions
248 · 10 sectionsWhat is the difference between a process and a thread, and what does it actually mean to say that each process has its own address space?
basics
~20 sA process owns an isolated address space and OS resources; threads are execution contexts inside one process that share that memory. Each thread has its own stack, registers and program counter. Separate processes cannot touch each other's memory and must use explicit inter-process communication.
Explain the difference between preemptive and cooperative scheduling of concurrent tasks, and what each model demands from the code being scheduled.
basics
~20 sUnder preemptive scheduling a timer interrupt lets the scheduler suspend a task at almost any instruction and run another. Under cooperative scheduling a task runs until it voluntarily yields. Preemption guarantees progress for everyone; cooperation is cheaper and predictable but one non-yielding task blocks all others.
Concurrency runtimes map the threads a program creates onto the threads an operating-system kernel actually schedules. Explain the 1:1, N:1 and M:N mappings, and what each one costs you.
basics
~20 sThree mappings of program threads onto kernel-scheduled threads. 1:1 — each program thread is a kernel thread: real parallelism, but costly. N:1 — many on one: cheap, but no multicore and one blocking call stalls all. M:N — both benefits, at the price of a complex two-level scheduler.
What does it mean to join a thread you started, and what problem does joining solve that simply starting the thread does not?
basics
~20 sJoining means one thread blocks until another finishes. Starting a thread only launches it; the starter keeps running and has no idea when the work is done or whether it succeeded. Join gives you completion timing and a safe point to read results.
What actually happens when the operating system switches from running one thread to another, and where does the cost of that switch come from?
basics
~20 sThe kernel saves the running thread's registers and program counter, picks another runnable thread, and restores its state; a switch to a different process also swaps page tables. The direct cost is small. The larger, indirect cost is cold caches, branch predictors and address-translation entries when the new thread starts running.
You have one coordinator thread that must not proceed until N independent worker tasks have each finished. Describe how a one-shot countdown latch solves this, and what happens to a thread that waits on a latch whose count has already reached zero.
basics
~20 sA latch is created with the count N. Each worker decrements it once when done; the coordinator blocks in await until the count reaches zero, then continues. The count never resets, so any wait after zero returns immediately instead of blocking.
A consumer thread must wait until a shared queue becomes non-empty before taking an item. Explain the monitor pattern — a mutex paired with a condition variable — and why it is preferred to a loop that sleeps briefly and re-checks the queue.
basics
~20 sA monitor is a lock protecting shared state plus a condition variable to wait on. The consumer locks, and while the queue is empty calls wait, which atomically releases the lock and sleeps. A producer adds an item under the lock and signals. Polling wastes CPU and adds latency; waiting costs nothing until woken.
What does a mutual-exclusion lock (a mutex) actually guarantee to the code that uses it, and what does it explicitly not guarantee?
basics
~20 sAt most one thread holds a given mutex, so critical sections guarded by that same lock never overlap, and a holder's writes become visible to the next holder. It promises nothing about acquisition order, fairness, deadlock freedom, or data guarded by another lock.
What is a semaphore in concurrent programming, and what is the difference between a counting semaphore and a binary semaphore?
basics
~20 sA semaphore is a counter of permits with two atomic operations: acquire, which takes a permit and blocks while none are free, and release, which returns one. A counting semaphore starts with N permits; a binary semaphore holds at most one.
Two threads each run the statement `counter = counter + 1` one thousand times on a shared variable, with no synchronization. The final value is often less than 2000. Explain why, using what the hardware actually executes.
basics
~20 sThat one statement is three machine steps: read the value, add one, write it back. Two threads can read the same old value, both add one to it, and both write the same new value — so two increments produce one. Updates are lost whenever the steps interleave.
What is a deadlock, and which four conditions must all hold at the same time for one to be possible?
basics
~10 sDeadlock is a set of threads permanently blocked, each waiting for a resource another one in the set holds. It requires four conditions at once: mutual exclusion, hold-and-wait, no preemption, circular wait.
Interviewers often distinguish a 'data race' from a 'race condition'. Define each precisely, and show that neither one implies the other.
basics
~20 sA data race is a memory-model property: two threads access the same location concurrently, at least one writes, with no ordering between them. A race condition is a correctness property: the result depends on timing. You can have a race condition with zero data races (all accesses properly synchronized, but the logic assumes state cannot change between steps) and a data race that never produces a wrong-looking answer.
Two threads transfer money between accounts, each locking the source account and then the destination. Explain why this can hang forever, how a global lock ordering fixes it, and what you do when the objects being locked have no natural order.
basics
~20 sTransfer(A,B) and transfer(B,A) take the two locks in opposite orders and can each hold one while waiting for the other. Fix: define one total order over all lockable objects, such as a unique id, and always acquire in that order.
What is livelock, and how does it differ from deadlock? Describe the runtime signature of each.
basics
~20 sBoth are liveness failures: no useful work completes. In deadlock the threads are blocked forever waiting on each other, so their states are frozen. In livelock the threads stay runnable and keep changing state (retry, back off, yield) but the pattern repeats and nobody finishes.
Two threads each run the statement `count = count + 1` a thousand times on the same shared variable, and the final total is less than two thousand. Explain why, and what it means for an operation to be an atomic read-modify-write.
basics
~20 sThe statement is three steps: read, add one, write back. Two threads can read the same old value and both write the same new value, so one increment is lost. An atomic read-modify-write performs all three steps as one indivisible hardware operation that no other thread can interleave with.
A worker thread spins in a loop reading an ordinary boolean 'stop' variable while another thread sets it to true and then exits. Sometimes the worker never leaves the loop. Explain how that is possible even though the write definitely executed.
basics
~20 sNothing orders the write against the read, so the reader has no obligation to observe it. The compiler may load the variable once into a register and loop on that copy, and the write may sit in the writer's store buffer. Publish the flag through a synchronization edge.
In a lock-free algorithm built on compare-and-swap, a thread reads a shared pointer whose value is A, does some work, and its later compare-and-swap from A succeeds — yet the structure ends up corrupted. Explain what went wrong and why a successful compare-and-swap was not enough.
basics
~20 sThat is the ABA problem. Between the read and the compare-and-swap, other threads changed the value to B and back to A. Compare-and-swap only checks that the word is equal now, not that nothing happened meanwhile, so anything the thread inferred from its first read may be stale.
Describe how a compare-and-swap instruction works and how you build an arbitrary atomic update on top of it with a retry loop. Why can the loop iterate more than once, and what happens to a thread that keeps losing?
basics
~20 sCompare-and-swap atomically writes a new value only if the location still equals the value you expected, reporting success or failure. You read, compute a new value, attempt the swap, and loop on failure with a fresh read. A loop iterates when another thread updated the location first; a persistently unlucky thread can starve, though the system as a whole always progresses.
Two threads each increment their own private counter, but the two counters are adjacent fields of the same object. Measured throughput is worse than one thread doing both increments, and it gets worse as you add cores. Explain what the hardware is doing and how you would confirm and fix it.
basics
~20 sFalse sharing. Coherence tracks whole cache lines, so both counters live in one line; each write must take exclusive ownership and invalidates the other core's copy, so the line ping-pongs between caches. Confirm with coherence-miss counters or by separating the fields; fix by padding or aligning them onto different lines, or by accumulating per thread.
What is a coroutine, and what actually happens when one suspends, compared with an operating-system thread that blocks waiting for I/O?
basics
~20 sA coroutine is a function that can pause partway through and resume later. Suspending saves its state, hands control back to a scheduler, and frees the underlying thread for other work. A blocked thread keeps its whole stack and OS slot while doing nothing.
What is an event loop, and what does run-to-completion mean for the callbacks it dispatches?
basics
~20 sAn event loop is a single thread cycling forever: wait for ready events, take the next task from a queue, run its handler to completion, repeat. No handler is ever interrupted mid-way by another handler, so loop-owned state needs no locks — but a slow handler delays everything behind it.
In asynchronous programming, what is the distinction between a future and a promise, and why do many libraries hand out two separate objects for a single asynchronous result?
basics
~20 sA future is the read side of a result that is not ready yet: you await it or attach a continuation. A promise is the write side: the producer completes it once, with a value or an error. Splitting them stops consumers from completing results.
When you build a pipeline from steps that each return a future, what is the difference between transforming a future's value with a plain mapping function and chaining a step that itself returns a future — and what breaks if you confuse the two?
basics
~20 sMapping applies a synchronous function to the value. Chaining applies a function that returns another future and flattens it, so the result completes when the inner work finishes. Mapping with an async function yields a future of a future: the outer completes early, before the real work is done.
How do failures travel through a chain of asynchronous steps built from futures, and why does wrapping the call in a try/catch at the call site usually fail to catch them?
basics
~20 sA future completes with either a value or an error, and an error short-circuits the downstream transforms until a handler that accepts errors is reached. A try/catch at the call site only sees synchronous failures, because the function returns a pending handle long before the failure exists.
Explain the difference between data parallelism and task parallelism, give a concrete example of each, and say what determines how far each one can scale.
basics
~20 sData parallelism runs the same operation over different slices of one data set — its scaling limit is how finely you can split the data. Task parallelism runs different operations concurrently — its scaling limit is the number of independent tasks and their dependency graph. Resizing a million images is data parallel; fetching a user profile and their orders at once is task parallel.
Describe the fork-join model of parallel computation: how does a unit of work split itself, and what does the 'join' step guarantee to the code that runs after it?
basics
~20 sA task checks whether its input is small enough to do directly. If not, it splits the input into independent pieces, forks them so they can run in parallel, then joins - waits for each piece to finish - and combines their results. Join gives completion and result visibility.
Explain the map, shuffle (regroup), and reduce phases of the map-reduce processing model: what does each phase do to the data, and why is the middle phase needed at all?
basics
~20 sMap transforms each input record independently into key-value pairs. Shuffle regroups those pairs so that all values for the same key land together on one reducer. Reduce folds each key's group into a result. Without the regroup step a reducer would only see part of each key.
Explain pipeline parallelism as a way to organize concurrent work: what a stage is, how items move between stages, and what determines how many items the arrangement finishes per second once it runs steadily.
basics
~20 sSplit a repeated job into ordered steps called stages, each with its own worker and a queue in front of it. Different items sit in different stages at the same time, so all workers run at once. Steady-state throughput is one item per slowest stage's time.
In a recursive divide-and-conquer parallel algorithm, why do implementations stop splitting once a subproblem is below some size and run the remainder sequentially, and how would you choose that threshold?
basics
~20 sEach split costs bookkeeping - creating, queueing and scheduling a task. Below some size that overhead exceeds the useful work, so the recursion would slow things down. Pick the threshold by measuring: choose the smallest size where parallel still beats sequential, comfortably above the break-even point.
A service runs its work on a fixed set of worker threads fed by a task queue. What happens when every worker is already busy and new tasks keep arriving, and how does that show up to the callers submitting work?
basics
~20 sNew tasks wait in the queue instead of running. Throughput stays flat at what the workers can serve, so latency climbs as the backlog grows. An unbounded queue eats memory; a bounded one eventually refuses work or blocks the submitter.
When an application is stopping, its worker pool may still hold queued tasks and tasks that are mid-execution. Describe the difference between an orderly shutdown and an immediate one, and what each does with those two categories of work.
basics
~20 sOrderly shutdown stops accepting new submissions but drains the queue and lets running tasks finish. Immediate shutdown also refuses new work, discards the queue - returning the undone tasks - and signals running tasks to stop. Both requests return at once; termination happens later.
You ask a scheduler to run a piece of work once, 100 milliseconds from now. Explain what that request actually guarantees, how a scheduler typically decides what to run next, and why the work might start noticeably later than 100 ms.
basics
~20 sA delay is a lower bound, not an appointment: the task will not start before 100 ms, but it may start much later. The scheduler keeps pending tasks ordered by due time and can only run one when a worker is free and the clock has passed it.
A service processes CPU-heavy tasks in a worker pool on an 8-core machine. A colleague proposes raising the pool from 8 workers to 200 to make it faster. Why does throughput usually not improve, and how can it get worse?
basics
~20 sOnly 8 tasks can actually compute at once, so throughput is already capped by the cores. Extra workers add context switches, cache pollution, memory for stacks and more lock contention. Throughput flattens then dips, and per-task latency rises because more work is in flight.
A task running on a bounded worker pool submits a second task to that same pool and then blocks waiting for the second task's result. Explain why this arrangement can deadlock, and what determines whether it actually will.
basics
~20 sThe waiting parent still occupies a worker. If every worker is a blocked parent, no worker is left to run the queued children, so parents wait on children that can never start. It is a resource deadlock where the scarce resource is the worker thread.
In a concurrent system, what does it actually mean to cancel a running task, and why is cancelling a task different from simply ignoring the result it eventually produces?
basics
~20 sCancelling means asking a running task to stop early and release what it holds; the task must notice the request and wind down. Ignoring its result stops nothing: it keeps burning CPU, connections and memory, and its side effects still happen.
A background task is started fire-and-forget — nobody keeps its handle or waits for it — and it throws. What typically happens to that error, and why is it dangerous?
basics
~20 sWith no owner waiting, the error has nowhere to propagate: it ends that task and lands in a default handler, an unread log line, or a result object nobody inspects. The system keeps reporting healthy while the work silently is not being done.
A developer starts a background task by spawning it and immediately returning, keeping no handle to it. What can go wrong with such fire-and-forget tasks?
basics
~20 sNobody owns it. Failures vanish silently, nobody waits for it, it can outlive the data and resources it borrowed, it cannot be cancelled or given a deadline, and shutdown can kill it mid-work. The leak stays invisible.
What is a cancellation checkpoint, and how would you make a 30-second CPU-bound computation loop cancellable? What determines how quickly cancellation actually takes effect?
basics
~20 sA checkpoint is a point where the task tests the cancellation signal and abandons the work if it is set. Blocking and awaiting operations usually provide them implicitly; a pure compute loop has none, so you poll the signal every N iterations. Cancellation latency is roughly the time between checkpoints plus cleanup time.
Why do modern concurrency runtimes make cancellation cooperative — the task must observe a signal — instead of forcibly terminating it from outside, and where does a thread-level interruption flag fit into that model?
basics
~20 sForced termination stops a task at an arbitrary instruction, so invariants are half-updated, locks and buffers are left in unknown states, and no cleanup runs. Cooperative cancellation stops only at safe points. A thread interruption flag is the same idea at thread level: a bit that blocking operations honour by aborting early.
Describe the actor model of concurrency: what an actor consists of, and why processing one message at a time removes the need for locks around an actor's state.
basics
~20 sAn actor is an isolated unit with private state, a mailbox, and behaviour. Actors never share memory; they only send each other asynchronous messages. Each actor processes its mailbox strictly one message at a time, so its state is never touched concurrently and needs no lock.
What is the CSP (Communicating Sequential Processes) model of concurrency, and how does coordinating tasks by passing values over channels differ from coordinating them with shared mutable state and locks?
basics
~20 sCSP models a program as independent sequential processes that share no memory and interact only by sending and receiving values on channels. The communication is itself the synchronization, so there is no shared mutable state to lock.
When concurrent tasks exchange data as messages, what properties must the message type have for any number of receivers to read it safely without coordinating, and what does immutable have to mean for a value that contains other objects inside it?
basics
~20 sThe message must be fully built before it is sent and never change afterwards, all the way down: nested objects, arrays and collections must be immutable too, and the sender must keep no reference it can mutate. Then every reader sees identical content regardless of timing.
Describe the producer-consumer pattern built around a bounded buffer: which threads block, under what conditions, and what problem the arrangement solves that a direct call would not.
basics
~20 sProducers put work items into a shared fixed-size buffer; consumers take them out. A producer blocks when the buffer is full, a consumer blocks when it is empty. This decouples the two sides in time and rate while the fixed capacity stops a fast producer from exhausting memory.
What message ordering and delivery guarantees do actor systems typically provide, what do they explicitly not provide, and how does that change the way you write message handlers?
basics
~20 sTypically at-most-once delivery, with FIFO order preserved only per sender-receiver pair. There is no global ordering, no causal ordering through intermediaries, and no guaranteed delivery. So handlers must tolerate lost, delayed and interleaved messages, and be idempotent if you add retries.
A colleague tests asynchronous code by starting the work, sleeping for 100 milliseconds, and then asserting the result. Why is that approach problematic, and what should the test wait on instead?
basics
~20 sA fixed sleep is a guess about duration. Too short and the test fails on a loaded machine; too long and the suite crawls. Wait on a real completion signal instead — a future, a latch, a callback, or a quiescence check — with a generous timeout.
What is a data race, and what does a dynamic race detector actually observe at runtime in order to decide that two memory accesses race?
basics
~20 sA data race is two threads accessing the same memory location, at least one of them writing, with no synchronization ordering the accesses. A dynamic detector instruments every load, store and synchronization event at runtime and checks whether each pair of conflicting accesses is ordered.
What is a virtual (fake) clock in a test suite, and which kinds of behaviour become testable when application code reads time through an injected clock instead of calling the system clock directly?
basics
~20 sA virtual clock is a time source the test controls: it only advances when the test says so. Injecting it lets you test timeouts, retry backoff, cache expiry, rate limiting, and scheduled jobs instantly and deterministically, with no real waiting.
A running service stops serving requests while its processor usage sits near zero. How do you use thread dumps — snapshots of every thread's stack and state — to work out what it is stuck on?
basics
~20 sTake several dumps a few seconds apart and compare. Near-zero processor use means nobody is running, so look for threads blocked on a lock (and who owns it), waiting on a condition or a remote response, and for whole worker pools stuck in the same stack — that shared frame is the culprit.
Simply running a multi-threaded test in a loop usually finds nothing. How would you design a stress harness that actually surfaces an interleaving bug?
basics
~20 sShrink the code under test so the bad window is a large fraction of it, start all threads from a barrier so they collide instead of running sequentially, perturb timing with randomized delays and varied thread counts, check a real invariant rather than a crash, and record the seed and observed state so a failure is reproducible.