HuntExams Academy logo
JavaAdvanced#concurrency

What is thread starvation?

Answer

A condition where a thread is perpetually denied access to shared resources because other higher-priority or more frequent threads keep acquiring the lock first, preventing it from making progress.

Example

// Low-priority thread never gets CPU time if high-priority threads constantly run
Thread lowPriority = new Thread(task);
lowPriority.setPriority(Thread.MIN_PRIORITY); // more prone to starvation

Detailed explanation

Thread starvation is a liveness problem in which a thread cannot make progress because it is repeatedly denied a resource it needs, such as CPU time, a lock, or an available executor worker. Other threads may continue running successfully, so the application can appear active while one operation waits indefinitely. Unlike ordinary contention, where threads wait temporarily and eventually proceed, starvation involves persistent unfairness or a scheduling arrangement that prevents progress.

In Java, starvation can arise when competing threads repeatedly acquire a lock before a waiting thread gets a chance. The synchronized mechanism does not guarantee first-come-first-served lock acquisition, so waiting longer does not automatically give a thread precedence. Long critical sections and heavy contention can increase the risk. CPU scheduling can also contribute: a low-priority platform thread may receive fewer execution opportunities when higher-priority threads remain runnable. However, setPriority(Thread.MIN_PRIORITY) does not prove that starvation will occur; priority behaviour depends on the JVM and operating system. Virtual threads have a fixed priority that cannot be changed through setPriority.

Starvation differs from deadlock because it does not require a cycle of threads waiting for resources held by one another. It also differs from livelock, where threads keep reacting or retrying without completing useful work. A related executor problem occurs when every worker in a bounded pool waits for tasks queued to that same pool. Those queued tasks cannot start because no worker is available; this is commonly called thread-pool starvation and can become a thread-starvation deadlock when the dependencies cannot resolve.

To reduce starvation, keep lock-holding periods short, avoid blocking operations while holding locks, and design executor dependencies carefully. ReentrantLock(true) favours the longest-waiting thread under contention, but fairness may reduce throughput and cannot guarantee fair CPU scheduling. Its untimed tryLock() can still bypass waiting threads. In interviews, explain the denied resource, identify why other threads keep winning access, and propose a mitigation appropriate to that resource rather than claiming that thread priorities alone solve the problem.

Key points to remember

  • Starvation means persistent lack of progress because a thread cannot obtain a required resource.
  • Unfair lock acquisition, CPU scheduling, and executor saturation can cause starvation.
  • Java synchronized blocks do not guarantee FIFO lock acquisition.
  • Thread priority is platform-dependent and is not a correctness or fairness guarantee.
  • Fair locks can reduce lock starvation, but they do not guarantee fair scheduling.

Common mistakes candidates make

  • Treating every long wait as starvation without distinguishing temporary contention from persistent lack of progress.
  • Claiming that a low-priority thread will definitely starve or that increasing priority guarantees progress.
  • Confusing starvation with deadlock, or assuming a fair lock eliminates every starvation scenario.

Likely follow-up questions

How is starvation different from deadlock?

In deadlock, a set of threads cannot proceed because of unresolved resource dependencies, typically a circular wait. In starvation, one thread may keep losing access while other threads continue making progress.

Does ReentrantLock(true) completely prevent starvation?

A fair ReentrantLock favours the longest-waiting thread under contention, reducing starvation caused by lock acquisition order. It cannot ensure fair CPU scheduling, and untimed tryLock() does not honour its fairness setting.

How would you investigate suspected thread starvation?

Compare repeated thread dumps with task latency, executor queue sizes, and per-thread CPU usage to identify operations that repeatedly fail to progress. Use profiling or Java Flight Recorder to investigate lock contention, long lock holders, or workers blocked on tasks queued to their own pool.

Interview tip: Define starvation as a liveness failure, give one lock-contention example, and qualify the priority example as a possibility rather than a guarantee.

Related Interview Questions

1
JavaIntermediate#java8#streams

What is the difference between map() and flatMap() in streams?

Open
2
JavaBeginner#streams

What is the difference between Collectors.toList() and Collectors.toSet()?

Open
3
JavaIntermediate#streams

How do you group elements using Collectors.groupingBy?

Open