Starvation
TL;DR
- Starvation occurs when a thread is perpetually denied access to the CPU or a required lock, preventing it from making any progress.
- Unlike a deadlock, the system as a whole hasn’t frozen, but one or more specific threads are effectively stuck.
- Often caused by unfair locks, thread priority abuse, or threads holding locks for too long.
Concept
If a parent distributes cookies to a group of children, and always gives cookies to the tallest children first, the shortest child might never get a cookie if the taller children keep coming back. The shortest child is “starving”.
In Java, if Thread A is constantly acquiring a lock, releasing it, and immediately acquiring it again, a slower Thread B might be stuck in the BLOCKED state forever. Thread B is starving.
Another common cause is setting Thread Priorities. If you have 10 threads with MAX_PRIORITY constantly doing heavy calculations, and 1 thread with MIN_PRIORITY, the OS scheduler might never assign CPU time to the low-priority thread.
Examples
public class StarvationExample {
public static void main(String[] args) {
// Shared lock
Object lock = new Object();
// This greedy thread takes the lock and rarely lets it go
Thread greedyThread = new Thread(() -> {
while (true) {
synchronized (lock) {
System.out.println("Greedy thread working...");
try { Thread.sleep(100); } catch (Exception e) {}
}
}
});
// This starving thread will rarely, if ever, get a chance to run
Thread starvingThread = new Thread(() -> {
while (true) {
synchronized (lock) {
System.out.println("Starving thread finally got the lock!");
}
}
});
greedyThread.setPriority(Thread.MAX_PRIORITY);
starvingThread.setPriority(Thread.MIN_PRIORITY);
starvingThread.start();
greedyThread.start();
}
}
Interview Questions
Q: How do you prevent thread starvation?
A: 1. Use Fair Locks: Instead of synchronized, use new ReentrantLock(true). A fair lock guarantees that the thread waiting the longest gets the lock next, establishing a strict queue.
2. Avoid Thread Priorities: Do not mess with Thread.setPriority(). Let the OS manage standard time-slicing.
3. Minimize Critical Sections: Ensure threads hold locks for the absolute minimum amount of time necessary. Don’t do heavy I/O operations (like downloading files) while holding a lock.