Thread Starvation
About
Starvation is a condition in multithreaded programming where a thread is perpetually denied access to necessary resources because other higher-priority threads are constantly acquiring them. This can lead to situations where a thread never gets a chance to execute, despite being in a runnable state.
Causes of Starvation
Thread Priority Inversion: If a high-priority thread keeps executing and the lower-priority thread never gets CPU time, the lower-priority thread starves.
Unfair Lock Mechanisms: When locks are not fairly assigned, certain threads may always acquire the lock before others.
Resource Hoarding: If a thread holds onto shared resources for an extended period, other threads may never get access.
Frequent Context Switching: If higher-priority threads frequently preempt a lower-priority thread, the lower-priority thread may not get enough execution time.
Example of Starvation
1. Due to High-Priority Threads
If high-priority threads keep running, a low-priority thread might not execute at all.
Problem in the Above Code
The low-priority thread may never execute if the high-priority threads keep running.
The CPU scheduler keeps selecting high-priority threads, ignoring lower-priority threads.
2. Due to Synchronized Blocks
If a thread monopolizes a synchronized block, other threads might starve.
Example Without Fair Synchronization
Problem
The low-priority thread might never acquire the lock as high-priority threads keep executing.
Preventing Starvation
1. Using Fair Locks (ReentrantLock)
We can use a ReentrantLock with the fairness policy enabled. This ensures that threads acquire the lock in the order they requested it, thus preventing starvation.
Solution
A fair lock ensures that threads are scheduled in FIFO order.
No thread is left waiting indefinitely.
The use of a fair ReentrantLock ensures that all threads, regardless of their priority, will eventually get a chance to execute, thus preventing any thread from being left waiting indefinitely.
Thread priority is a hint to the thread scheduler, and it might not always be respected. The actual execution order of threads depends on the thread scheduler of the JVM and the underlying operating system. Using a fair ReentrantLock can help ensure that no thread is left waiting indefinitely, regardless of their priority.
2. Using Thread.yield()
Thread.yield()
Calling Thread.yield()
allows lower-priority threads to execute.
Solution
Calling
Thread.yield()
hints the scheduler to give CPU time to other waiting threads.
3. Using wait()
and notify()
for Cooperative Scheduling
wait()
and notify()
for Cooperative SchedulingInstead of always grabbing CPU, threads can release control using wait()
.
Solution
The lower-priority thread voluntarily waits instead of continuously trying to execute.
notify()
ensures that it gets a chance to run.
Starvation vs Deadlock
Cause
High-priority threads blocking low-priority ones
Multiple threads waiting for each other indefinitely
Resolution
Use fair scheduling, locks, or yield()
Avoid nested locks, use tryLock()
, timeout mechanisms
Impact
Some threads may never execute
All threads involved are blocked
Last updated
Was this helpful?