skip to content

What is the difference between deadlock prevention and deadlock avoidance, and how does the banker's algorithm decide whether to grant a resource request?

level: middleimportance: nice to knowfreq 26%

answer

  1. prevention = design time; avoidance = request time
  2. max claim declared up front; need = max - allocation
  3. safe sequence: finish someone, recycle, repeat
  4. unsafe is not deadlocked, just not provably fine
  5. unused in practice; survives as admission control

basics

~20 s

Prevention removes one of the four necessary conditions structurally, so deadlock cannot arise. Avoidance allows all four but refuses any request that would leave an unsafe state. The banker's algorithm grants a request only if a safe completion sequence still exists.

solid answer

~60 s

**Prevention** is static: you design so that one Coffman condition never holds - a global lock order (no circular wait), acquiring the whole set atomically (no hold-and-wait), immutability (no mutual exclusion), abortable acquisition (no preemption). No runtime information is needed and no request is ever refused. **Avoidance** is dynamic: all four conditions may hold, but each request is examined before granting, and requests that could lead to deadlock are delayed. The **banker's algorithm** is the canonical avoidance scheme. Each participant declares its maximum claim up front. The system tracks allocation, remaining need (max minus allocated) and available instances. On a request it tentatively grants, then runs a safety check: is there an ordering in which some participant's remaining need fits within available, finishes, and returns its holdings - repeated until all finish? If such a **safe sequence** exists, the grant stands; otherwise the requester waits. Unsafe does not mean deadlocked, only that deadlock becomes possible. It is rarely used in practice: maximum claims are unknown, participant and resource sets are dynamic, and the check costs roughly O(n squared times m) per request.

code

text · 10 lines
text
work    = Available
finish  = [false for each participant]

repeat:
    pick i with finish[i] == false and Need[i] <= work
    if none exists: break
    work += Allocation[i]      # pretend i completes and releases
    finish[i] = true

safe = all(finish)             # a safe sequence exists

go deeper

for a junior

State the difference: prevention changes the design so a condition cannot hold, avoidance checks each request at runtime and may make the requester wait.

for a middle

Walk the safe-sequence check over available, allocation and need, and explain that unsafe is not the same as deadlocked.

for a senior

Explain why the assumptions fail in real systems and name the practical descendants: acquire-all-up-front, admission control, bounded borrow depth.

for a principal

Frame it as a utilization-versus-guarantee trade: conservative admission buys completion guarantees at the cost of throughput, which is the same call you make when sizing pools and quotas.

## Three strategies, not two The classic taxonomy has four responses to deadlock: - **Prevention** - design so one of the four necessary conditions can never hold. - **Avoidance** - allow the conditions, but use advance knowledge of future requests to refuse grants that could lead to trouble. - **Detection and recovery** - let it happen, find it, break it by aborting a victim. - **Ignore it** - accept the risk and restart when it bites (the "ostrich" approach, which most general-purpose operating systems take for user-level locks). Prevention and avoidance are frequently confused in interviews, and the distinction is exactly *when* the decision is made. Prevention decides at design time and needs no runtime state: a lock order either exists in the code or it does not. Avoidance decides at request time and needs runtime accounting plus a declaration of future intent. ## The state model behind the banker's algorithm The algorithm treats the system like a bank lending identical units of several currencies: - **Available[j]** - free instances of resource type j. - **Max[i][j]** - the most of type j that participant i will *ever* need at once, declared before it starts. - **Allocation[i][j]** - what i currently holds. - **Need[i][j] = Max[i][j] - Allocation[i][j]** - what i may still ask for. A state is **safe** if there exists an ordering of all participants - a *safe sequence* - such that each in turn can have its entire remaining need satisfied from what is currently available plus everything released by those before it. If such a sequence exists, the system can always drive every participant to completion, so deadlock is impossible from that state. ## The two procedures **Safety check.** Start with `work = Available` and everyone unfinished. Repeatedly look for an unfinished participant whose `Need` is componentwise less than or equal to `work`; pretend it runs to completion and add its `Allocation` back into `work`; mark it finished. If every participant can be finished this way, the state is safe; if you get stuck with unfinished participants, it is unsafe. **Request handling.** When participant i requests a vector R: 1. If R exceeds `Need[i]`, it broke its declared maximum - an error. 2. If R exceeds `Available`, i simply waits for resources. 3. Otherwise tentatively apply the grant (`Available -= R`, `Allocation[i] += R`, `Need[i] -= R`) and run the safety check. If the resulting state is safe, keep the grant; if unsafe, roll the grant back and make i wait. ## Safe, unsafe, deadlocked These are three distinct states, and mixing them up is the standard mistake. Safe means deadlock cannot occur from here regardless of what participants do next. **Unsafe does not mean deadlocked** - it means that if everyone requested up to their declared maximum, the system might not be able to finish anyone. Many unsafe states never deadlock, because participants do not actually claim their maximum. Avoidance is therefore deliberately **conservative**: it refuses grants that would have been fine, trading utilization for a guarantee. ## Why it is not used in real systems The assumptions are severe: - Every participant must declare its **maximum claim in advance**. Real programs discover what they need as they run. - The set of participants and resources must be **fixed and known**. Threads are created dynamically, and locks are created per object. - Resources must be **countable interchangeable instances**. A mutex protecting a specific object is not interchangeable with any other mutex. - The safety check runs on **every request**, at roughly O(n squared times m) for n participants and m resource types. - A refused request means the participant **waits**, so a poor claim declaration wrecks throughput. So it stays a teaching device that makes the safe-state concept precise. That concept, however, does survive in practice. ## Where the idea does show up - **Admission control**: refuse a new job unless the resources it declares are available, so accepted work is guaranteed to complete. - **Reserve-the-whole-set up front**: acquire every permit or lock an operation needs before starting it, and take nothing if the full set is unavailable - the hold-and-wait prevention that avoidance's claim declaration hints at. - **Capacity guards and quotas** in schedulers and cluster managers, which will not place work unless a feasible completion plan exists. - **Bounded borrow depth**: capping how many pool permits a single unit of work may hold is a crude but effective safe-state rule.

  • Is an unsafe state the same as a deadlocked state?
    No. Unsafe means there is no guaranteed sequence in which every participant can finish if each claims up to its declared maximum, so deadlock has become possible. Deadlocked means a circular wait already exists and progress has stopped. Plenty of unsafe states run to completion without incident because participants never actually request their full maximum, which is exactly why avoidance is conservative and costs utilization.
  • What practical technique is the closest working relative of the banker's algorithm?
    Acquiring the entire resource set an operation needs before it begins, and taking nothing at all if the full set is not available. That removes hold-and-wait outright, uses the same idea of declaring your requirements up front, and needs no safety computation. Admission control in schedulers and capping how many pool permits one unit of work may hold are the same principle applied at a coarser grain.

saying these in an interview costs you the question

  • Describing the banker's algorithm as detection - it decides before granting, never after a cycle exists.
  • Claiming an unsafe state means the system is already deadlocked.
  • Presenting it as a practical technique for mutexes, ignoring that locks are not interchangeable countable instances.
  • Forgetting that every participant must declare a maximum claim in advance for the algorithm to work at all.
  • Calling avoidance a form of prevention; prevention negates a condition structurally, avoidance permits all four and refuses risky grants.

context