A deadlock is a situation where two or more tasks each wait for a resource that another task is holding, so none of them can ever proceed and the whole thing freezes. Picture two people who each need both a pen and paper: one grabs the pen, the other grabs the paper, and now each refuses to hand over what they have until they get the other item. Neither can finish, and they wait forever. In software, the tasks are usually threads or processes and the resources are locks, files, or database rows. A deadlock is not a crash; it is a permanent, silent standstill, which is what makes it so frustrating to debug.
How a deadlock happens
Deadlocks arise in concurrent programs, where multiple things run at the same time and share resources. To keep shared data safe, a task locks a resource before using it. The trouble starts when tasks need more than one lock and grab them in different orders.
Thread A: locks Resource 1, then waits for Resource 2
Thread B: locks Resource 2, then waits for Resource 1
-- both wait forever; neither releases what it holds
Each thread is being perfectly reasonable on its own. The deadlock is an emergent property of the timing between them, which is why it can appear intermittently and be hard to reproduce.
The four conditions
A classic result says a deadlock can only occur when all four of these hold at once. Break any one and you prevent it.
| Condition |
Meaning |
| Mutual exclusion |
A resource can be held by only one task at a time |
| Hold and wait |
A task holds one resource while waiting for another |
| No preemption |
A resource cannot be forcibly taken away |
| Circular wait |
A cycle of tasks each waiting on the next |
The circular wait is usually the easiest to attack. If every task acquires locks in the same global order, no cycle can form, and the deadlock cannot occur.
How to prevent and handle deadlocks
A few practical habits prevent most deadlocks in everyday code.
- Use a consistent lock order. Decide a global order for acquiring locks and never deviate. This alone kills the circular-wait condition.
- Hold fewer locks, for less time. The less you lock and the sooner you release, the smaller the window for trouble.
- Use timeouts. If a task cannot get a lock within a reasonable time, have it back off and retry instead of waiting forever.
- Prefer higher-level tools. Concurrency libraries, queues, and database transaction managers often handle locking more safely than hand-rolled locks.
Databases detect many deadlocks automatically and resolve them by aborting one transaction so the other can finish; your code then retries the loser. This connects to broader concurrency pitfalls, so the guide on what a race condition is is a useful companion.
Deadlock, livelock, and starvation
These get confused. A deadlock is frozen: nothing moves. A livelock is the opposite kind of stuck: tasks keep reacting to each other and changing state but make no real progress, like two people repeatedly stepping aside in the same direction. Starvation is when a task can run in principle but is perpetually skipped because others keep jumping ahead. All three are concurrency hazards, but only a deadlock is a true standstill.
What to skip
- Grabbing many locks at once. Minimize the number of simultaneous locks; each one adds risk.
- Inconsistent lock ordering. This is the single most common cause; pick an order and stick to it everywhere.
- Waiting without a timeout. Indefinite waits turn a transient conflict into a permanent freeze.
- Hand-rolling locking when a tool exists. Battle-tested concurrency primitives are safer than ad hoc locks.
FAQ
What is a deadlock in simple terms?
A standstill where two or more tasks each wait for a resource the other holds, so none can ever finish.
What are the four conditions for a deadlock?
Mutual exclusion, hold and wait, no preemption, and circular wait. All four must hold at once, so breaking any one prevents the deadlock.
How do you prevent a deadlock?
Acquire locks in a consistent global order, hold as few locks as briefly as possible, use timeouts, and prefer proven concurrency tools.
Is a deadlock the same as a livelock?
No. A deadlock is completely frozen. A livelock keeps changing state but makes no useful progress, so tasks are busy yet stuck.
Where to go next
What is a race condition, What is async await, and What is a memory leak.