---
title: Multithreading and Concurrency Interview Questions
description: Multithreading and concurrency interview questions with answers — race conditions, deadlocks, locks vs atomics, thread pools, and async vs threads explained.
url: https://usegreenroom.app/blog/multithreading-concurrency-interview-questions
last_updated: 2026-08-01
---

← Back to blog

Technical

# Multithreading interview questions: find the shared mutable state

August 1, 2026 · 13 min read

![Multithreading and concurrency interview questions and answers — guide cover from Greenroom, the AI mock interviewer](/assets/blog/multithreading-concurrency-interview-questions-hero.webp)

The counter incremented from two threads and the tests passed a hundred times. It shipped. Three weeks later the reported totals were consistently, slightly low, in a way nobody could reproduce locally, and the cause was one line that read a value, added one, and wrote it back — three operations that the developer, and the test suite, had both been treating as one.

**Multithreading and concurrency interview questions** appear in almost every backend, systems and senior loop, and they reward a specific habit: naming the shared mutable state before reasoning about anything else. Every concurrency bug lives there. This guide covers the six areas interviewers actually ask about.

## Concurrency versus parallelism

The distinction that scores: **concurrency is dealing with many things at once; parallelism is doing many things at once.** Concurrency is a structural property — a single-core machine can run concurrent tasks by interleaving them. Parallelism requires multiple cores and is about simultaneous execution.

Then the practical consequence: an IO-bound workload benefits from concurrency without any parallelism, because the threads are mostly waiting. A CPU-bound workload needs real parallelism to go faster. That framing answers half the follow-ups in this round.

![Diagram of concurrency interview topics — concurrency versus parallelism, race conditions, deadlock conditions, synchronisation primitives, thread pools and async versus threads](/assets/blog/multithreading-concurrency-interview-questions-diagram.webp)

Six areas. The one candidates fumble is deadlock — not the definition, but the four conditions and which one you would actually break.

## Race conditions

A race condition is when correctness depends on the timing of threads. The two classic shapes:

- **Read-modify-write.** `counter = counter + 1` is a load, an add and a store. Two threads can interleave so one increment is lost. This is the opening story.
- **Check-then-act.** `if (!map.containsKey(k)) map.put(k, v);` — another thread can insert between the check and the act. `putIfAbsent` exists precisely for this.

The important framing: **races are non-deterministic, so passing tests proves nothing.** Interviewers like hearing that, because it explains why these bugs reach production.

The fixes, in increasing cost: make the state immutable so there is nothing to race on; confine it to one thread; use an atomic operation; or take a lock.

```code:java
// three ways to fix the same bug, in increasing order of cost
int counter = 0;                              // broken: load, add, store

AtomicInteger atomic = new AtomicInteger();
atomic.incrementAndGet();                     // lock-free compare-and-set loop

synchronized (lock) { counter++; }            // correct, but serialises every caller

// best of all where possible: do not share mutable state
// give each thread its own counter and sum them at the end
```

## Deadlock, and the four conditions

Everyone knows the definition. The question that separates candidates is: **what are the conditions, and which one do you break?**

The four necessary conditions are mutual exclusion, hold-and-wait, no preemption, and circular wait. All four must hold simultaneously, so preventing any one prevents deadlock.

In practice you break **circular wait**, by acquiring locks in a globally consistent order — for example always locking the account with the lower ID first in a transfer. That is the answer to the standard "two threads transferring money between two accounts" question, and it is the answer interviewers are listening for.

Secondary techniques worth naming: lock timeouts with retry and backoff, using a single coarser lock rather than two fine ones, and lock-free structures where feasible.

Also know the neighbours: **livelock**, where threads keep responding to each other and make no progress, and **starvation**, where a thread never gets scheduled. And know that timeouts turn a deadlock into a slow failure rather than a hang, which is often the pragmatic production answer.

## Synchronisation primitives

- **Mutex / lock** — one holder at a time. Keep the critical section as small as possible; a lock held across a network call is a classic production incident.
- **Semaphore** — allows N holders. The right tool for limiting concurrent access to a resource such as a connection pool.
- **Read-write lock** — many readers or one writer. Good for read-heavy state, with the caveat that writer starvation is possible and that the extra bookkeeping can make it slower than a plain mutex for short critical sections.
- **Atomics and compare-and-set** — hardware-level operations that avoid locking entirely, and the basis of most lock-free structures.
- **Condition variables** — waiting for a state change without spinning. Always wait in a loop, because spurious wakeups exist.
- **Volatile / memory visibility** — this is the subtle one. Without a memory barrier, one thread's write may not be visible to another at all, regardless of ordering. `volatile` in Java guarantees visibility but not atomicity, which is why `volatile int i; i++;` is still broken. That specific question is asked often.

Our [Java interview questions](/blog/java-interview-questions) guide covers the language-level details.

## Thread pools

- **Why a pool at all?** Thread creation is expensive and unbounded thread creation exhausts memory. A pool bounds concurrency and reuses threads.
- **Sizing.** For CPU-bound work, roughly the number of cores — more threads just add context switching. For IO-bound work, many more than the core count, because threads are mostly blocked waiting.
- **The queue is the dangerous part.** An unbounded queue means work piles up invisibly until you run out of memory, converting a throughput problem into an outage. Bounded queues plus an explicit rejection policy are the correct default, and saying this is a strong signal.
- **Separate pools for separate concerns**, so a slow downstream dependency cannot starve unrelated work. This is the bulkhead pattern.
- **Never block a pool thread on a task submitted to the same pool** — a guaranteed deadlock and a real interview question.

## Async versus threads

Increasingly asked, and often answered vaguely.

Async or event-loop concurrency runs tasks cooperatively on a small number of threads, switching at explicit await points. It scales to very large numbers of concurrent IO operations cheaply, because there is no per-connection thread stack.

The critical caveat: **a blocking call inside an event loop blocks everything.** One synchronous database driver in an async service stalls every other request on that loop. This is the single most common async bug and worth stating unprompted.

Threads are simpler for CPU-bound work and for code that is already blocking. Modern runtimes blur this — virtual threads in Java and goroutines in Go give you a blocking programming model with async-like scaling. Naming those shows you are current.

Python is worth a specific note if it comes up: the **GIL** means threads do not give you CPU parallelism, so threads help for IO-bound work and multiprocessing is the answer for CPU-bound work. Our [Python interview questions](/blog/python-interview-questions) guide covers it.

## Design-level concurrency

Senior loops move from primitives to design:

- **Avoid shared mutable state entirely** where you can — immutability, message passing, per-thread state. This is the most valuable answer available.
- **Idempotency**, so retries in a concurrent system are safe.
- **Optimistic versus pessimistic concurrency** in databases: a version column and a conditional update, versus row locking. Know when each is right.
- **The producer-consumer pattern** with a bounded queue, which is the standard answer to "how would you decouple these two stages?"
- **Distributed locks** and their honest danger — a lock lease can expire while you still believe you hold it, so the protected operation must be idempotent or fenced. Our [distributed systems interview questions](/blog/distributed-systems-interview-questions) guide covers this.

**The core truth:** every concurrency answer should start by naming the shared mutable state. If there is none, there is no bug. If there is, the whole conversation is about who may touch it and when.

## Questions you should expect verbatim

- "What is the difference between concurrency and parallelism?"
- "What is a race condition and how do you prevent one?"
- "What causes a deadlock and how do you avoid it?"
- "What is the difference between a mutex and a semaphore?"
- "How do you size a thread pool?"
- "What is the difference between `volatile` and `synchronized`?"

Our [operating system interview questions](/blog/operating-system-interview-questions) guide covers scheduling and context switching underneath all of this.

## Where each prep option actually helps

- **Java Concurrency in Practice** — still the best book on this topic even if you do not write Java, because the mental model transfers.
- **The Little Book of Semaphores** — free, and excellent for the classic synchronisation problems interviewers reuse.
- **Write one broken program on purpose** — an unsynchronised counter, then a deadlock with two locks, then fix both. It makes the answers yours rather than remembered.
- **Your language's concurrency documentation** — the Go memory model, Java's `java.util.concurrent`, or Python's asyncio, depending on your stack.
- **ChatGPT** — good for explaining a memory model question. It will not notice that your critical section wraps a network call, which is the thing that pages you at 3am.
- **Greenroom** — the spoken layer. [Ari, the AI interviewer](/) runs backend rounds out loud and pushes past the definition to the follow-up. Honest tradeoff: Ari will not run your threads, so write the broken program yourself.

## Frequently asked questions

### What is the difference between concurrency and parallelism?

Concurrency is dealing with many things at once and is a structural property, so a single core can run concurrent tasks by interleaving them. Parallelism is doing many things at once and requires multiple cores. The practical consequence is that IO-bound workloads benefit from concurrency alone because threads are mostly waiting, while CPU-bound workloads need genuine parallelism to run faster.

### What is a race condition and how do you prevent it?

A race condition is when correctness depends on the timing of threads, most commonly in read-modify-write sequences such as incrementing a counter, or check-then-act sequences such as checking a key is absent before inserting it. Fixes in increasing order of cost are making the state immutable, confining it to a single thread, using an atomic operation such as compare-and-set, or taking a lock. Because races are non-deterministic, passing tests proves nothing, which is why they reach production.

### What causes a deadlock and how do you avoid it?

Deadlock requires four conditions to hold simultaneously — mutual exclusion, hold-and-wait, no preemption and circular wait — so preventing any one of them prevents deadlock. In practice you break circular wait by acquiring locks in a globally consistent order, such as always locking the lower account ID first in a transfer. Secondary techniques include lock timeouts with backoff, using one coarser lock instead of two fine ones, and lock-free structures.

### What is the difference between a mutex and a semaphore?

A mutex allows exactly one holder at a time and is intended for protecting a critical section, whereas a semaphore permits up to N concurrent holders and is intended for limiting access to a pool of resources such as database connections. A read-write lock is a third option allowing many readers or one writer, useful for read-heavy state but with possible writer starvation and enough bookkeeping overhead that a plain mutex can be faster for very short critical sections.

### How do you size a thread pool?

For CPU-bound work use roughly the number of available cores, since additional threads only add context-switching overhead. For IO-bound work use substantially more than the core count because threads spend most of their time blocked. Just as important is the queue: an unbounded queue lets work pile up invisibly until memory is exhausted, so a bounded queue with an explicit rejection policy is the correct default, and separate pools per concern prevent one slow dependency starving unrelated work.

### What is the difference between volatile and synchronized?

Volatile guarantees visibility — a write by one thread becomes visible to others — but it does not provide atomicity, which is why incrementing a volatile integer is still broken since the increment is a load, an add and a store. Synchronized provides both mutual exclusion and visibility by establishing a happens-before relationship, at the cost of serialising callers. For a simple counter the better answer than either is an atomic type using compare-and-set.

Concurrency rounds go one follow-up past the definition, every time. [Greenroom](https://usegreenroom.app/) runs backend rounds out loud with Ari, who asks the next question. Free to start. Curious how it works? See [how AI mock interviews work](/blog/ai-mock-interview).
