In this article
  1. Foundations
  2. Locks and signalling
  3. Deadlock and liveness
  4. Language runtimes and design patterns
  5. Sources

Foundations

What is the difference between concurrency and parallelism?

Concurrency is structuring a program as tasks that can make progress in overlapping time periods; parallelism is running tasks at the same instant on more than one core. A single core can run concurrent tasks by switching between them. The Python threading documentation says threading still suits tasks that wait on input and output, while processes suit work that keeps the processor busy.

Why it is asked: it separates design from hardware. Related: the system design hub; read the full explanation: parallelism and concurrency.

What is the difference between a process and a thread?

A process is a running program with its own memory; a thread is a path of execution inside a process that shares that memory with its sibling threads. Sharing makes communication cheap, and it is also the source of races. A crash or corrupt write in one thread can damage the whole process, while processes are isolated from one another.

Why it is asked: it checks the model under every other answer. Read the full explanation: multithreading and multiprocessing.

What is a race condition?

A race condition is a bug in which the result depends on the order in which threads interleave. The classic case is an unprotected counter: each thread reads the value, adds one and writes it back. With two threads that increment once each there are 6 possible orders of the four steps, and 4 of them end with 1 instead of 2.1 With two increments each, 64 of the 70 orders give a wrong total.1

Why it is asked: it tests whether you can reason about interleavings. Related: optimistic and pessimistic locking for concurrent updates.

What are atomicity and order violations?

Atomicity and order violations are the two common non-deadlock concurrency bugs. An atomicity violation breaks the intended all-or-nothing nature of a group of steps, and an order violation happens when two operations are meant to run in one order but nothing enforces it. Lu and colleagues (2008) studied 105 concurrency bugs in four large open-source programs, and 74 of them were not deadlocks (Operating Systems: Three Easy Pieces, chapter 32).

Why it is asked: most real bugs are not deadlocks.

What does it mean for an operation to be atomic?

An atomic operation appears to other threads as a single indivisible step: they see it either not started or finished. In Python, the library FAQ lists L.append(x) and D[x] = y as atomic, and i = i+1 and D[x] = D[x] + 1 as not, because the latter perform several steps between which threads can switch (Python FAQ).

Why it is asked: it exposes the read-modify-write mistake.

Locks and signalling

What is the difference between a mutex and a semaphore?

A mutex lets one thread at a time into a section of code; a semaphore holds a counter of permits and lets that many threads in. The Python threading documentation says a semaphore's counter drops on each acquire and rises on each release, and an acquire blocks when it is zero. A semaphore with one permit behaves like a mutex without an owner.

Why it is asked: interviewers often ask for the difference between a mutex and a semaphore as a definition check. Related: lock_guard and scoped_lock in C++.

What is a reentrant lock, and when do you need one?

A reentrant lock can be acquired again by the thread that already holds it, which a plain lock cannot. The Python threading documentation says an RLock must be released by the thread that acquired it and that its acquire and release pairs may be nested, unlike a Lock's. You need one when a locked method calls another locked method of the same object, as plain locks would deadlock the thread against itself.

Why it is asked: it tests lock ownership.

How does a condition variable work, and why wait in a loop?

A condition variable lets a thread sleep until another thread signals that a shared condition may have changed. The wait releases the lock and blocks in one atomic step, and the Portable Operating System Interface (POSIX) specification warns that spurious wakeups may occur. Also, another thread can change the state before the woken thread retakes the lock; the Python threading docs say the condition "may no longer hold true". So re-test the predicate in a while loop.

Why it is asked: the loop is the detail that separates answers.

How would you build a bounded blocking queue?

Keep a buffer, a lock and two conditions: not full and not empty. A producer takes the lock, waits in a loop while the buffer is full, adds an item and signals not empty. A consumer mirrors it. In production Python code, use queue.Queue, which the library FAQ recommends for handing work between threads because it does the locking itself.

Why it is asked: it combines locks, conditions and invariants.

How do an Event and a Barrier differ?

An Event is a flag that threads wait on until another thread sets it; once set, all waiters pass. A Barrier makes a fixed number of threads wait for each other, and none proceeds until all have arrived. The Python threading page documents both: the event's wait blocks until the flag is true, and the barrier releases its threads together.

Why it is asked: it checks knowledge of coordination tools beyond locks.

Knowing it is half the job. Saying it is the other half.

Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.

Start Free Trial →

Deadlock and liveness

What is a deadlock?

A deadlock is a situation in which two or more threads are blocked forever, each waiting for another. Oracle's Java tutorial shows two friends who bow to each other: each method is synchronized, each thread holds one friend's lock and waits for the other's, and neither can leave. The tutorial calls the hang "extremely likely" and not certain, because it depends on scheduling (Oracle's deadlock tutorial).

Why it is asked: interviewers follow code with "Where can this deadlock?". Related: database deadlocks and lock order.

What are the four conditions for deadlock?

Mutual exclusion, hold-and-wait, no preemption and circular wait. Threads need exclusive control of a resource, hold one while asking for another, cannot have it taken away, and form a cycle of waiting. All four must hold; remove any one and deadlock cannot occur (Operating Systems: Three Easy Pieces, citing the 1971 survey by Coffman and colleagues).

Why it is asked: it turns a vague fear into a checklist.

How do you prevent a deadlock?

Break one of the four conditions; the most practical, as the operating systems textbook cited below puts it, is circular wait, by taking locks in one agreed global order. In a small enumeration of two threads that take locks A and B, opposite orders deadlocked in 2 of 6 allowed schedules, a third, and the same order in 0 of 2.1 Other ways are acquiring everything at once, try-locks with back-off, or avoiding locks.

Why it is asked: it tests whether you can fix, not only name, the bug. Related: lock order in a bank transfer.

Opposite lock order deadlocks in a third of schedulesBar chart of the share of allowed schedules that deadlock for two threads and two locks: 33 percent when the threads take the locks in opposite order and 0 percent when both take them in the same order.Schedules that deadlock (%)010203040OppositeSameLock order33.302 of 6Diagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/concurrency-interview-questions.html
Taking locks in one agreed order removes the circular wait, so the deadlock cannot happen. The 33 percent is 2 of 6 allowed schedules, a count and not a probability.CC BY 4.0 — source: Concurrency interview questions on race conditions, locks, deadlock and asyncio
Text description of the diagram
  • Opposite lock order deadlocked in 2 of 6 allowed schedules.
  • The same lock order deadlocked in 0 of 2 allowed schedules.

What is the difference between deadlock, livelock and starvation?

In a deadlock the threads are blocked. In a livelock they keep running but only respond to each other, so none progresses: Oracle's example is two people in a corridor who keep stepping aside in the same direction. In starvation a thread cannot get regular access to a shared resource because greedy threads hold it for long periods (Oracle's livelock tutorial).

Why it is asked: it checks vocabulary for liveness failures.

Language runtimes and design patterns

Does the Python GIL make threaded code safe?

No. The global interpreter lock (GIL) ensures that only one thread runs Python bytecode at a time, and it makes each single bytecode step atomic, but a statement such as D[x] = D[x] + 1 is several steps, so threads can interleave inside it. The FAQ's advice is "When in doubt, use a mutex!": put a lock around compound updates (Python FAQ).

Why it is asked: "Does the GIL change your answer?" is a common follow-up to threaded code. Read the full explanation: the global interpreter lock.

When would you use threads, processes or asyncio in Python?

Use threads or asyncio for tasks that mostly wait on input and output, and processes for work that keeps the processor busy. The Python threading documentation says that in CPython only one thread executes Python code at once, so for processor-bound work it advises multiprocessing or a process pool. Asyncio runs many tasks on one thread, switching only at an await.

Why it is asked: it tests matching the model to the workload. Read the full explanation: why Python threads do not speed up processor-heavy code.

What is free-threaded Python?

Free-threaded Python is a build of CPython without the GIL, so threads can run Python code in parallel on several cores. The Python free-threading guide says support starts with 3.13, that you can check a build with sysconfig.get_config_var("Py_GIL_DISABLED"), and that some third-party extension modules may not be ready and will re-enable the GIL. Thread safety of your own shared data is still your job.

Why it is asked: it tests current knowledge. Read the full explanation: free-threaded Python and parallelism.

Why does a blocking call freeze an asyncio program, and how do you fix it?

Asyncio's event loop runs one task at a time and switches only when a task awaits, so a blocking call inside a coroutine stalls every other task. The documentation shows that calling a blocking function directly blocks the loop for its duration, and asyncio.to_thread runs it in a separate thread instead, which suits input and output work (Python asyncio docs).

Why it is asked: it tests whether you know what the event loop does while a call blocks.

What is the difference between gather and a TaskGroup in asyncio?

Both run awaitables concurrently, but they handle failure differently. With gather the first exception propagates to the caller while the other awaitables are not cancelled and keep running. A TaskGroup cancels the remaining tasks when one fails, which the documentation describes as stronger safety for nested subtasks (Python asyncio docs).

Why it is asked: it tests structured concurrency.

What does "share memory by communicating" mean?

"Share memory by communicating" is a Go design slogan: do not coordinate threads by sharing data guarded by locks; pass data between goroutines through channels so that only one has access at a time. The Go blog's 2010 example replaces a mutex-protected shared structure and its bookkeeping fields with a poller that receives from one channel and sends to another (Go Blog, July 2010).

Why it is asked: it tests knowledge of alternatives to locks.

What is compare-and-swap, and when is it better than a lock?

Compare-and-swap is a low-level atomic operation that sets a variable to a new value only if it still holds an expected value. Java's atomic classes offer lock-free thread-safe updates of single variables, with operations such as compareAndSet and getAndIncrement (Java Platform, Standard Edition (SE) 21 documentation). It suits one variable; a multi-variable invariant still needs a lock.

Why it is asked: it separates lock-free from lock-based designs. Related: ConcurrentHashMap and atomic updates in Java.

Key takeaways
  • A race condition is a bug that depends on thread interleaving; with two threads and one unprotected increment each, 4 of 6 orders lose an update.
  • Deadlock needs four conditions; taking locks in one global order removes circular wait.
  • Wait on a condition in a loop, because wakeups can be spurious.
  • The Python GIL does not make compound updates safe, and free-threaded builds drop it.
  • Asyncio switches only at an await, so move blocking calls to a thread.

Sources

  1. threading: Thread-based parallelism (Python 3.15 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  2. Coroutines and Tasks (Python 3.15 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  3. Python support for free threading (Python documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  4. Library and Extension FAQ: thread-safe mutation (Python documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  5. pthread_cond_wait (POSIX.1-2024, Issue 8) — The Open Group (IEEE Std 1003.1-2024, Issue 8; page gives only the copyright range 2001-2024; read 2026-10-24), 24 Oct 2026
  6. Deadlock (The Java Tutorials, Concurrency) — Oracle (page shows no date; read 2026-10-24), 24 Oct 2026
  7. Starvation and Livelock (The Java Tutorials, Concurrency) — Oracle (page shows no date; read 2026-10-24), 24 Oct 2026
  8. java.util.concurrent.atomic package summary (Java SE 21) — Oracle (read 2026-10-24), 24 Oct 2026
  9. Share Memory By Communicating (The Go Blog), Andrew Gerrand — The Go Project, 13 Jul 2010
  10. Common Concurrency Problems, chapter 32 of Operating Systems: Three Easy Pieces (Arpaci-Dusseau) — Arpaci-Dusseau Books (copyright 2008-26; read 2026-10-24), 24 Oct 2026