Deadlock, Starvation, and Livelock
Last Updated :
11 Jul, 2025
Deadlock, starvation, and livelock are problems that can occur in computer systems when multiple processes compete for resources. Deadlock happens when processes get stuck waiting for each other indefinitely, so none can proceed. Starvation occurs when a process is repeatedly denied access to resources because others with higher priority keep getting them first. Livelock is when processes keep changing their states to avoid conflict but still fail to make progress, similar to two people constantly stepping aside for each other without passing. Understanding these issues is important to design systems that run smoothly without getting stuck.
Deadlock
A deadlock is a situation where a set of processes is blocked because each process is holding a resource and waiting for another resource acquired by some other process. In this article, we will discuss deadlock, its necessary conditions, etc. in detail.
- Deadlock is a situation in computing where two or more processes are unable to proceed because each is waiting for the other to release resources.
- Key concepts include mutual exclusion, resource holding, circular wait, and no preemption.
DeadlockProcess 1 is holding Resource 1 and waiting for resource 2 which is acquired by process 2, and process 2 is waiting for resource 1.
Starvation
Starvation is the problem that occurs when high priority processes keep executing and low priority processes get blocked for indefinite time. In heavily loaded computer system, a steady stream of higher-priority processes can prevent a low-priority process from ever getting the CPU.
Causes of Starvation :
- Priority Scheduling: If there are always higher-priority processes available, then the lower-priority processes may never be allowed to run.
- Resource Utilization: We see that resources are always used by more significant priority processes and leave a lesser priority process starved.
Livelock
Livelock occurs when two or more processes continually repeat the same interaction in response to changes in the other processes without doing any useful work. These processes are not in the waiting state, and they are running concurrently. This is different from a deadlock because in a deadlock all processes are in the waiting state.
LIVELOCKThe diagram illustrates a livelock scenario in an operating system where two processes (Process A and Process B) are actively trying to perform an action (fork()
), but repeatedly fail because the process table is full.
Example: Imagine a pair of processes using two resources below:
Java
/*package whatever //do not write package name here */
void processA()
{
enterReg(resource1);
enterReg(resource2);
useBothResources();
leaveReg(resource2);
leaveReg(resource1);
}
void processB()
{
enterReg(resource1);
enterReg(resource2);
useBothResources();
leaveReg(resource2);
leaveReg(resource1);
}
C++
void process_A(void)
{
enter_reg(&resource_1);
enter_reg(&resource_2);
use_both_resources();
leave_reg(&resource_2);
leave_reg(&resource_1);
}
void process_B(void)
{
enter_reg(&resource_1);
enter_reg(&resource_2);
use_both_resources();
leave_reg(&resource_2);
leave_reg(&resource_1);
}
Each of the two processes needs the two resources and they use the polling primitive enterReg to try to acquire the locks necessary for them. In case, the attempt fails, the process just tries again. If process A runs first and acquires resource 1 and then process B runs and acquires resource 2, no matter which one runs next, it will make no further progress, but neither of the two processes blocks. What actually happens is that it uses its CPU quantum over and over again without any progress being made but also without any sort of blocking. Thus, this situation is not that of a deadlock( as no process is being blocked) but we have something functionally equivalent to a deadlock: LIVELOCK.
What leads to Livelock
Livelock occurs when processes continuously change their state in response to each other, but make no actual progress unlike a deadlock, where they’re stuck waiting.
A classic cause is competition for finite resources, such as process table entries in a UNIX system. For example, imagine a system with 100 process slots. Ten programs each try to create 12 subprocesses. After each has spawned 9, all 100 slots are used (10 parents + 90 children). When the programs try to create more processes, fork()
fails due to the full table.
If each program waits a random time and tries again, they may all continuously fail and retry endlessly reacting but never succeeding. This is livelock: the system is active but makes no progress. Though rare, such situations are possible and difficult to detect.
Difference between Deadlock, Starvation and Livelock
A livelock is similar to a deadlock, except that the states of the processes involved in the livelock constantly change with regard to one another, none progressing. Livelock is a special case of resource starvation; the general definition states that a specific process is not progressing.
Feature | Deadlock | Starvation | Livelock |
---|
Definition | Processes are blocked forever, each waiting for a resource held by another. | A process waits indefinitely because it is always bypassed by others. | Processes keep executing but fail to make progress. |
Cause | Circular wait and resource holding. | Unfair resource allocation or scheduling. | Processes continuously respond to each other, preventing progress. |
Process State | Blocked (not executing). | Ready but not scheduled/executed. | Actively executing but not making progress. |
System Progress | No progress at all. | System progresses, but some processes do not. | System is busy, but no real work is done. |
Example | A waits for B’s resource; B waits for A’s resource. | A low-priority task never gets CPU time. | Two processes constantly yielding to each other. |
Resolution | Requires deadlock detection and recovery. | Use of fair scheduling (e.g., aging). | Needs better coordination or back-off strategies. |
Livelock:
CPP
var l1 = .... // lock object like semaphore or mutex etc
var l2 = .... // lock object like semaphore or mutex etc
// Thread1
Thread.Start( ()=> {
while (true)
{
if (!l1.Lock(1000))
{
continue;
}
if (!l2.Lock(1000))
{
continue;
}
/// do some work
});
// Thread2
Thread.Start( ()=> {
while (true)
{
if (!l2.Lock(1000))
{
continue;
}
if (!l1.Lock(1000))
{
continue;
}
// do some work
});
Java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private static final Lock l1 = new ReentrantLock();
private static final Lock l2 = new ReentrantLock();
public static void main(String[] args)
{
// Thread1
new Thread(() -> {
while (true) {
try {
if (!l1.tryLock(1000)) {
continue;
}
if (!l2.tryLock(1000)) {
l1.unlock();
continue;
}
// do some work
l2.unlock();
l1.unlock();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();
// Thread2
new Thread(() -> {
while (true) {
try {
if (!l2.tryLock(1000)) {
continue;
}
if (!l1.tryLock(1000)) {
l2.unlock();
continue;
}
// do some work
l1.unlock();
l2.unlock();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();
}
}
Deadlock:
CPP
var p = new object();
lock(p)
{
lock(p)
{
// deadlock. Since p is previously locked
// we will never reach here...
}
Java
public class Main {
static final Object lock1 = new Object();
static final Object lock2 = new Object();
public static void main(String[] args)
{
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock1...");
try {
Thread.sleep(100); } catch (InterruptedException ignored) {}
System.out.println("Thread 1: Waiting for lock2...");
synchronized (lock2) {
System.out.println("Thread 1: Acquired lock2!");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock2...");
try {
Thread.sleep(100); } catch (InterruptedException ignored) {}
System.out.println("Thread 2: Waiting for lock1...");
synchronized (lock1) {
System.out.println("Thread 2: Acquired lock1!");
}
}
});
t1.start();
t2.start();
}
}
Starvation:
CPP
Queue q = .....
while (q.Count > 0)
{
var c = q.Dequeue();
.........
// Some method in different thread accidentally
// puts c back in queue twice within same time frame
q.Enqueue(c);
q.Enqueue(c);
// leading to growth of queue twice then it
// can consume, thus starving of computing
}
Java
/*package whatever //do not write package name here */
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static void main(String[] args)
{
Queue<Object> q = new LinkedList<>();
while (!q.isEmpty()) {
Object c;
synchronized (q)
{
c = q.poll(); // Dequeue
// Do something with c
}
// Some method in a different thread
// accidentally puts c back in the queue twice
// within the same time frame
synchronized (q)
{
q.offer(c); // Enqueue once
q.offer(c); // Enqueue again
}
// This can lead to the growth of the queue
// twice as fast as it can consume, potentially
// causing starvation of computing resources
}
}
}
Starvation happens when "greedy" threads make shared resources unavailable for long periods.
Similar Reads
Operating System Tutorial An Operating System(OS) is a software that manages and handles hardware and software resources of a computing device. Responsible for managing and controlling all the activities and sharing of computer resources among different running applications.A low-level Software that includes all the basic fu
4 min read
OS Basics
Process & Threads
CPU Scheduling
Deadlock
Memory & Disk Management
Memory Management in Operating SystemMemory is a hardware component that stores data, instructions and information temporarily or permanently for processing. It consists of an array of bytes or words, each with a unique address. Memory holds both input data and program instructions needed for the CPU to execute tasks.Memory works close
7 min read
Fixed (or static) Partitioning in Operating SystemFixed partitioning, also known as static partitioning, is one of the earliest memory management techniques used in operating systems. In this method, the main memory is divided into a fixed number of partitions at system startup, and each partition is allocated to a process. These partitions remain
8 min read
Variable (or Dynamic) Partitioning in Operating SystemIn operating systems, Memory Management is the function responsible for allocating and managing a computerâs main memory. The memory Management function keeps track of the status of each memory location, either allocated or free to ensure effective and efficient use of Primary Memory. Below are Memo
4 min read
Paging in Operating SystemPaging is the process of moving parts of a program, called pages, from secondary storage (like a hard drive) into the main memory (RAM). The main idea behind paging is to break a program into smaller fixed-size blocks called pages.To keep track of where each page is stored in memory, the operating s
8 min read
Segmentation in Operating SystemA process is divided into Segments. The chunks that a program is divided into which are not necessarily all of the exact sizes are called segments. Segmentation gives the user's view of the process which paging does not provide. Here the user's view is mapped to physical memory. Types of Segmentatio
4 min read
Segmentation in Operating SystemA process is divided into Segments. The chunks that a program is divided into which are not necessarily all of the exact sizes are called segments. Segmentation gives the user's view of the process which paging does not provide. Here the user's view is mapped to physical memory. Types of Segmentatio
4 min read
Page Replacement Algorithms in Operating SystemsIn an operating system that uses paging for memory management, a page replacement algorithm is needed to decide which page needs to be replaced when a new page comes in. Page replacement becomes necessary when a page fault occurs and no free page frames are in memory. in this article, we will discus
7 min read
File Systems in Operating SystemA computer file is defined as a medium used for saving and managing data in the computer system. The data stored in the computer system is completely in digital format, although there can be various types of files that help us to store the data.File systems are a crucial part of any operating system
8 min read
File Systems in Operating SystemA computer file is defined as a medium used for saving and managing data in the computer system. The data stored in the computer system is completely in digital format, although there can be various types of files that help us to store the data.File systems are a crucial part of any operating system
8 min read
Advanced OS
Practice