Thread Deadlock

A deadlock is a situation where a thread is waiting for an object lock that another thread holds, and this second thread is waiting for an object lock that the first thread holds. Since each thread is waiting for the other thread to relinquish a lock, they both remain waiting forever in the Blocked-for-lock-acquisition state. The threads are said to be deadlocked.

Basically, deadlock describes a situation where two or more threads are blocked forever, waiting for each other. It occurs when multiple threads need the same locks but obtain them in different order. A Java multi-threaded program may suffer from the deadlock condition because the synchronized keyword causes the executing thread to block while waiting for the lock, or monitor, associated with the specified object.

Java does not provide any mechanisms for detection or control of deadlock situations, so the programmer is responsible for avoiding them. Below is a program that illustrates deadlocks in multithreading applications:

ThreaTestDeadlock.java

public class ThreaTestDeadlock {

public static Object object1 = new Object();
public static Object object2 = new Object();

public static void main(String args[]) {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
}

private static class Thread1 extends Thread {

Thread1() {
System.out.println("Thread1 started..");
start();
}

public void run() {
synchronized (object1) {
System.out.println("Thread 1: Holding object 1...");

try {
Thread.sleep(500);
}
catch (InterruptedException e) {
System.out.println("Exception: " + e);
}
System.out.println("Thread 1: Waiting for object 2...");

synchronized (object2) {
System.out.println("Thread 1: Holding objects 1 & 2...");
}
}
System.out.println("Thread1 ended..");
}
}

private static class Thread2 extends Thread {

Thread2() {
System.out.println("Thread2 started..");
start();
}

public void run() {
synchronized (object2) {
System.out.println("Thread 2: Holding object 2...");

try {
Thread.sleep(500);
}
catch (InterruptedException e) {
System.out.println("Exception: " + e);
}
System.out.println("Thread 2: Waiting for object 1...");

synchronized (object1) {
System.out.println("Thread 2: Holding objects 1 & 2...");
}
}
System.out.println("Thread2 ended..");
}
}

}

 

When we compile and execute the above program, we find a deadlock situation and following is the output produced by the program −

Output:

Thread1 started..
Thread 1: Holding object 1...
Thread2 started..
Thread 2: Holding object 2...
Thread 1: Waiting for object 2...
Thread 2: Waiting for object 1...

 

Explanation

In this example, the above program will hang forever because neither of the threads is in the position to proceed nor waiting for each other to release the object lock, so we have to come out of the program by pressing CTRL + C.

In the next section, we will try to solve this problem.

 

Deadlock Solution Example

Let us change the order of the object lock and run the modified program below to see if both the threads still wait for each other −

ThreaTestDeadlock.java

public class ThreaTestDeadlock {

public static Object object1 = new Object();
public static Object object2 = new Object();

public static void main(String args[]) {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
}

private static class Thread1 extends Thread {

Thread1() {
System.out.println("Thread1 started..");
start();
}

public void run() {
synchronized (object1) {
System.out.println("Thread 1: Holding object 1...");

try {
Thread.sleep(500);
}
catch (InterruptedException e) {
System.out.println("Exception: " + e);
}
System.out.println("Thread 1: Waiting for object 2...");

synchronized (object2) {
System.out.println("Thread 1: Holding objects 1 & 2...");
}
}
System.out.println("Thread1 ended..");
}
}

private static class Thread2 extends Thread {

Thread2() {
System.out.println("Thread2 started..");
start();
}

public void run() {
synchronized (object1) {
System.out.println("Thread 2: Holding object 2...");

try {
Thread.sleep(500);
}
catch (InterruptedException e) {
System.out.println("Exception: " + e);
}
System.out.println("Thread 2: Waiting for object 1...");

synchronized (object2) {
System.out.println("Thread 2: Holding objects 1 & 2...");
}
}
System.out.println("Thread2 ended..");
}
}

}

 

 

Now by changing the order of the object locks prevent the program in going into a deadlock situation and completes with the following result −

Output:

Thread1 started..
Thread 1: Holding object 1...
Thread2 started..
Thread 1: Waiting for object 2...
Thread 1: Holding objects 1 & 2...
Thread1 ended..
Thread 2: Holding object 2...
Thread 2: Waiting for object 1...
Thread 2: Holding objects 1 & 2...
Thread2 ended..

 

Conclusion

The above example is used to make the concept of deadlock clear, however, it is a complex concept and we should deeply analyze it before developing our own application to handle any deadlock situation.