Difference between livelock and deadlock

Difference between livelock and deadlock

In Java multithreading, both livelock and deadlock are undesirable scenarios that can occur when multiple threads interact with each other. Here’s how they differ:

Difference between livelock and deadlock

1. Deadlock

  • Deadlock occurs when two or more threads are blocked forever, each waiting for the other to release a resource that they need. As a result, none of the threads can proceed, and the application becomes unresponsive.
  • Deadlock typically arises due to a circular dependency in resource acquisition, where each thread holds one resource and waits for another resource held by another thread.
  • Deadlock can be avoided by ensuring that threads acquire locks in a consistent order across all threads.

2. Livelock

  • Livelock is a situation where two or more threads are actively trying to resolve a resource conflict, but none make progress because they keep responding to each other’s actions without achieving their goal.
  • In a livelock scenario, threads are not blocked, but they are unable to make progress due to continuously changing states in response to each other’s actions.
  • Livelock can occur when threads try to avoid deadlock by releasing acquired resources when they cannot obtain all required resources, leading to a repeated cycle of resource acquisition and release without progress.

Now, let’s illustrate the difference between livelock and deadlock with a Java example:

Example
java
public class LivelockVsDeadlockExample {
    static class Spoon {
        private Diner owner;

        public Spoon(Diner owner) {
            this.owner = owner;
        }

        public synchronized void use() {
            System.out.println(owner.name + " has eaten");
        }

        public synchronized void setOwner(Diner diner) {
            this.owner = diner;
        }
    }

    static class Diner {
        private String name;
        private boolean isHungry;

        public Diner(String name) {
            this.name = name;
            this.isHungry = true;
        }

        public void eatWith(Spoon spoon, Diner otherDiner) {
            while (isHungry) {
                // Livelock scenario
                if (spoon.owner != this) {
                    try {
                        Thread.sleep(100); // Simulate thinking
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }

                // Try to acquire the other diner's spoon
                if (!otherDiner.isHungry) {
                    spoon.setOwner(this);
                    continue;
                }

                // Deadlock scenario
                spoon.use();
                spoon.setOwner(otherDiner);
                isHungry = false;
            }
        }
    }

    public static void main(String[] args) {
        Spoon spoon1 = new Spoon(new Diner("Alice"));
        Spoon spoon2 = new Spoon(new Diner("Bob"));

        Diner alice = new Diner("Alice");
        Diner bob = new Diner("Bob");

        // Livelock scenario
        Thread thread1 = new Thread(() -> alice.eatWith(spoon1, bob));
        Thread thread2 = new Thread(() -> bob.eatWith(spoon2, alice));

        thread1.start();
        thread2.start();
    }
}

In this Example

we have a Spoon class representing a shared resource (a spoon), and a Diner class representing a thread that interacts with the spoon. The eatWith() method of the Diner class simulates a scenario where diners try to eat with a shared spoon.

The eatWith() method illustrates both livelock and deadlock scenarios:

In the livelock scenario, diners keep swapping the spoon between each other without making progress towards eating. In the deadlock scenario, diners are blocked waiting for each other to release the spoon they need, resulting in a deadlock situation.

Homepage

Readmore