Creating and resolving Deadlocks in Java


Reading time: 25 minutes | Coding time: 10 minutes

📝 Table of Contents


⭐ What is a Deadlock ?


The term Deadlock refers to a situation in multithreading, where two or more threads are blocked forever, waiting for each other.
It is a simply a condition where a set of threads are blocked because each thread is holding a resource and waiting for another resource acquired by some other thread.
Since, all the threads are waiting for each other to release the resource, the condition is called Deadlock.

topic of image

In the above image, Thread 1 is waiting for Thread 2 to relase some resource, while Thread 2 is waiting for Thread 1 to release some resource. Hence, a deadlock is created between them.

⭐ synchronized keyword -


In Java, we use synchronized keyword to make the class or method thread-safe.The keyword ensures that only one thread can access the resource at a given point of time.

Generally, In Java, Deadlock is caused because of the synchronized keyword, because it blocks the executing thread to wait for the lock.

⭐ synchronized block -


In Java, synchronized blocks are used for creating threads and synchronizing their tasks. Such blocks are marked with synchronized keyword.

A synchronized block in Java is synchronized on some object. The block ensures that only one thread executes inside it at a time. All other threads attempting to enter it are blocked until the thread inside it exits.

General form of a synchronized block -

synchronized(sync_object)
{
// Access shared variables and other
// shared resources
}

⭐ Example of a Deadlock in Java -


public class CreateDeadlock {  
public static void main(String[] args) {  
 final String resource1 = "Resource 1";  
 final String resource2 = "Resource 2";  
 // Thread 1 tries to lock Resource 1 and then Resource 2   
 Thread t1 = new Thread() {  
    public void run() {  
        synchronized (resource1) {  
        System.out.println("Thread 1: I have locked Resource 1");    
        try { Thread.sleep(50);} catch (Exception e) {}  
        synchronized (resource2) {  
        System.out.println("Thread 1: I have locked Resource 2");  }  
    }  
}  
};  
// Thread 2 tries to lock Resource 2 then Resource 1  
    Thread t2 = new Thread() {  
    public void run() {  
        synchronized (resource2) {  
        System.out.println("Thread 2: I have locked Resource 2 ");  
        try { Thread.sleep(50);} catch (Exception e) {}  
        synchronized (resource1) {  
        System.out.println("Thread 2: I have locked Resource 2");  
    }  
}  
}  
};  
t1.start();  
t2.start();  
}  
} 

Output :

topic of image

Let's see step by step what is happening here -

  1. Thread 1 starts and acquires lock on resource 1.
  2. Thread 2 starts and acquires lock on resource 2.
  3. Thread 1 prints "Thread 1: I have locked Resource 1" and Thread 2 prints "Thread 2: I have locked Resource 2 ".
  4. Both of the threads wait for 50ms, so that both threads can be started if any of them is not.
  5. Thread 1 tries to take object lock of resource 2 but it is already acquired by Thread 2 so it waits till it become free. It will not release lock of Resource 1 until it gets lock of Resource 2.
  6. And same happens with thread 2. It tries to take object lock of resource 1 but it is already acquired by Thread 1 so it waits till it become free. It will not release lock of Resource 2 until it gets lock of Resource 1.
  7. Now, both threads are in wait state, and are waiting for each other to release the locks.
  8. As none of the thread is ready to release lock, so this is the Dead Lock condition.
  9. And hence when we run the program, it seems as if the program execution has stopped.

Diagrammatic representation of the Deadlock -

topic of image

⭐ Resolving the deadlock -


To resolve this deadlock, we can simply change the order of acquiring the locks.
Let's try this -

public class ResolveDeadlock {  
public static void main(String[] args) {  
    final String resource1 = "Resource 1";  
    final String resource2 = "Resource 2";  
    // Thread 1 tries to lock Resource 2 and then Resource 1  
    Thread t1 = new Thread() {  
    public void run() {  
        synchronized (resource2) {  
        System.out.println("Thread 1: I have locked Resource 2");  
        try { Thread.sleep(50);} catch (Exception e) {}  
        System.out.println("Thread 1: I am waiting for Resource 1"); 
        synchronized (resource1) {  
        System.out.println("Thread 1: I have locked Resource 1 and 2");  
           }  
    }  
}  
};  
// Thread 2 tries to lock Resource 2 and then Resource 1  
    Thread t2 = new Thread() {  
    public void run() {  
        synchronized (resource2) {  
        System.out.println("Thread 2: I have locked Resource 2 ");  
        try { Thread.sleep(50);} catch (Exception e) {}  
        System.out.println("Thread 2: I am waiting for Resource 1"); 
        synchronized (resource1) {  
        System.out.println("Thread 2: I have locked Resource 1");  
        }  
    }  
}  
};  
t1.start();  
t2.start();  
}  
} 

Output :

topic of image

And our deadlock has been successfully resolved ! 🎉

⭐ Questions-


1. Which keyword is used to make resources thread-safe ?

notify
wait
sleep
synchronized
synchronized keyword is used to attain a lock on a resource, and make it thread-safe.

2. The threads go into which state if they do not get the desired resource ?

active
waiting
terminated
resumed
The thread goes into waiting state, if it does not get its desired resource.