skip to content

Livelock & Starvation

Livelock means threads keep reacting to each other and never progress; starvation means one thread never gets the lock or the CPU. Interviewers ask for these to check you know deadlock is not the only way concurrency stalls.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is a 'liveness failure' in concurrent programming, and how do deadlock, livelock, and starvation differ?

level: juniorimportance: must knowfreq 70%

answer

  1. Liveness = forward progress; failure = no progress, no crash
  2. Deadlock = stuck + idle (cycle)
  3. Livelock = stuck + busy (mutual reaction/retry)
  4. Starvation = runnable but never gets its turn (unfairness)
  5. Two axes: blocked vs running, and cycle vs reaction vs unfairness

basics

~20 s

A liveness failure means threads never make progress. Deadlock: threads are blocked forever waiting on each other. Livelock: threads keep changing state reacting to each other but get nothing done. Starvation: one thread is perpetually denied a resource it needs.

solid answer

~50 s

Liveness is the property that a program eventually does something useful; a liveness failure is when it stops making progress even though it isn't crashing. There are three classic kinds. Deadlock: two or more threads are each blocked waiting for a lock the other holds, so all are stuck and idle. Livelock: threads are not blocked — they actively run and change state in response to one another — but their combined behavior keeps them looping without progress (like two people repeatedly stepping aside in a hallway). Starvation: a thread is runnable but is perpetually denied the CPU or a lock it needs, usually because others keep winning, e.g. unfair scheduling or thread priorities. The key contrast: deadlock threads are stuck and idle; livelock threads are stuck but busy; a starved thread could run but never gets its turn.

go deeper

for a junior

Can state the one-line definition of each: deadlock = blocked forever in a cycle, livelock = busy but no progress, starvation = never gets its turn.

for a middle

Distinguishes them on the two axes (blocked vs running; cycle vs reaction vs unfairness) and gives a concrete code or hallway example for each.

for a senior

Explains how a naive deadlock fix produces livelock, connects starvation to scheduling/priority and fairness, and reasons about CPU-usage symptoms when diagnosing.

for a principal

Frames liveness vs safety as formal program properties, discusses detectability/tooling, and weighs design choices (fair locks, randomized backoff, bounded retries) against throughput across a system.

## What 'liveness' means In concurrency, properties of a program are split into two families. A **safety** property says 'nothing bad ever happens' (e.g. no two threads corrupt the same data). A **liveness** property says 'something good eventually happens' — the program keeps making forward progress and completes its work. A **liveness failure** is therefore a bug where the program does not crash and does not corrupt data, but **stops making progress**. These are dangerous precisely because nothing looks broken: no exception, no crash, just a hang or a thread that never finishes. A **thread** is an independent path of execution. A **lock** (or **mutex**) is a token only one thread can hold at a time, used to protect shared data; a thread that wants a held lock must **block** (wait, doing nothing) until it is released. The **scheduler** is the part of the OS/JVM that decides which runnable thread gets a CPU core next. There are three classic liveness failures: ### 1. Deadlock Two or more threads each hold a lock and each wait for a lock the other holds, forming a cycle of waiting that never breaks. Example: Thread A holds lock 1 and wants lock 2; Thread B holds lock 2 and wants lock 1. Neither can proceed, neither will release, so both are **blocked forever and idle** (using no CPU). This is the most famous liveness failure. ### 2. Livelock Threads are **not blocked** — they keep running and **actively change their state in response to each other**, but the system as a whole makes **no progress**. The classic image: two people meet in a narrow hallway; each politely steps to the same side to let the other pass, then both step back, then both step the same way again — forever. In code, this often arises from a too-clever deadlock 'fix': a thread that can't get all the locks it needs releases what it holds, backs off, and retries — but if all contending threads do the same thing in lockstep, they keep releasing and retrying in sync and none ever acquires everything. Unlike deadlock, livelocked threads burn CPU. ### 3. Starvation A single thread is **perpetually denied a resource it needs** — CPU time or a lock — even though it is ready to run. It isn't waiting in a cycle (not deadlock) and isn't reacting to others (not livelock); it simply never wins. Common causes: **unfair scheduling** (the resource is repeatedly handed to other threads), **thread priorities** (low-priority threads can be starved by a steady stream of high-priority ones on some platforms), or a greedy thread that holds a shared resource for very long stretches. The starved thread could make progress if it ever got its turn — but it never does. ### The contrast table | | Threads are… | CPU usage | Cause | |---|---|---|---| | Deadlock | blocked, idle | none | circular lock-wait | | Livelock | running, busy | high | symmetric reaction/retry | | Starvation | runnable, denied | low for the victim | unfair allocation / priority | ### How to derive your answer Start from 'liveness = forward progress.' Then for each failure ask two questions: *Is the stuck thread blocked or running?* and *Is it stuck because of a cycle, mutual reaction, or simple unfairness?* Deadlock = blocked + cycle; livelock = running + mutual reaction; starvation = runnable but denied + unfairness. That two-axis mental model lets you classify any liveness bug you meet.

  • Which of the three failures consumes CPU while stuck, and why?
    Livelock. The threads are not blocked — they keep executing and changing state in response to each other, so they actively spin. Deadlocked threads are blocked and idle; a starved thread simply doesn't get scheduled.
  • Can starvation occur without any locks at all?
    Yes. Pure CPU starvation needs no locks — a runnable low-priority thread can be perpetually passed over by the scheduler in favor of busy higher-priority threads.

saying these in an interview costs you the question

  • Saying livelock is just deadlock — livelocked threads are actively running, not blocked
  • Claiming all three pin the CPU — only livelock typically burns CPU; deadlock is idle
  • Treating starvation as a cycle of waiting; it's about being perpetually denied, not a mutual wait
  • Confusing a liveness failure with a crash or data corruption (those are safety/availability issues)

context

open as a page

How can a well-intentioned fix for deadlock (releasing locks and retrying) actually cause livelock, and how do you prevent it?

level: middleimportance: must knowfreq 60%

basics

~20 s

If a thread that can't get all its locks releases them and retries, and every competing thread does the same in lockstep, they keep releasing and retrying together and none ever finishes. Fix it by adding randomness or backing off for different amounts of time.

open as a page

You're told a service is 'hung.' How would you tell whether it's suffering from deadlock, livelock, or starvation?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Check CPU usage and thread dumps. Near-zero CPU with threads BLOCKED in a cycle on each other's locks = deadlock. High CPU but no progress = livelock. The system works but one specific thread never advances = starvation.

open as a page

What causes thread starvation in Java, and how do fairness settings and thread priorities affect it?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Starvation happens when a thread never gets a resource it needs. In Java it can come from a thread holding a lock too long, unfair locks that let other threads barge ahead, or low thread priority making the scheduler skip it. Using a fair lock can prevent lock starvation.

open as a page

When designing a high-throughput concurrent system, how do you weigh fairness, backoff, and lock-free design to avoid liveness failures without crippling throughput?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Avoid sharing locks where you can: prefer lock-free or per-thread/per-shard data and work queues. Where you must lock, use a consistent lock order to prevent deadlock, keep critical sections tiny, and only turn on fairness or backoff where a starved or livelocked path actually hurts, since both cost throughput.

open as a page