How Deadlock occurs and how to fix it in Java

Deadlock describes a situation where two or more threads are blocked forever, waiting for each other. Java deadlock situation arises with at least two threads(multitasking or multi-threading) and two or more resources.

There are several ways you can detect deadlock in Java application.

The following points may give you ideas how to detect deadlock in application code:

  • look for nested synchronized blocks
  • check if are calling a synchronized method from another
  • if you are trying to lock different object

Another way to find out deadlock using thread dump. To take thread dump in Linux system you can execute command kill -3. In Windows system you can do so by pressing Ctrl + Break from the command line tool. This will print status of all threads and you can check which thread is locked on which object.

Another option is to use jConsole/VisualVM, which comes with JDK. The jconsole.exe is found under bin folder of JDK installation. You just need to double click on it. Connect to the Local Process under New Connection and click on connect button.

This is just an example and for actual application you have to find out your actual program to check the deadlock.

java deadlock fix

If you prompt for the insecure connection then click on insecure connection.

java deadlock fix

Next you will find few tabs and click on the Threads tab to check the lock on the thread objects.

java deadlock fix

Now let’s see how deadlock occurs in the program. This is the simple example and in actual application there may be complex scenario where deadlock occurs.

Class Thread1

public class Thread1 extends Thread {

	@Override
	public void run() {
		synchronized (Person.class) {
			System.out.println("Thread1: Holding lock on Person");

			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			System.out.println("Thread1: Waiting for lock on Address");

			synchronized (Address.class) {
				System.out.println("Thread1: Holding lock on Person and Address");
			}
		}
	}

}

Class Thread2

public class Thread2 extends Thread {

	@Override
	public void run() {
		synchronized (Address.class) {
			System.out.println("Thread2: Holding lock on Address");

			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			System.out.println("Thread2: Waiting for lock on Person");

			synchronized (Person.class) {
				System.out.println("Thread2: Holding lock on Address and Person");
			}
		}
	}

}

Testing

public class ThreadApp {

	public static void main(String[] args) {
		Thread1 thread1 = new Thread1();
		Thread2 thread2 = new Thread2();

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

}

The above class produces the below output:

Thread2: Holding lock on Address
Thread1: Holding lock on Person
Thread2: Waiting for lock on Person
Thread1: Waiting for lock on Address

If run() methods in Thread1 and Thread2 classes will be called by two or many threads, there is a good chance of occurring deadlock situation because if thread 1 acquires lock on Person object while executing run() and thread 2 acquires lock on Address object while running run() method, both will be waiting for each other to release lock on Person and Address to proceed further which will never happen.

Check deadlock in jConsole/VisualVM:

deadlock fix in java

Now click on Threads tab once connected and click on Detect Deadlock or Deadlock tab on the bottom.

Clicking on each thread (Thread-0 or Thread-1), you will see the following output:

fix deadlock in java

Now we will see how to avoid deadlock or how to resolve the deadlock situation.

If you look at the above code minutely, then you may figure out that the real reason for the deadlock is not two or more threads are requesting resources or lock but the way they are requesting a lock creates deadlock situation. Therefore if you provide ordered access then the problem will be resolved.

Update the run() method in the Thread2 class as follows:

@Override
public void run() {
	synchronized (Person.class) {
		System.out.println("Thread2: Holding lock on Address");

		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("Thread2: Waiting for lock on Person");

		synchronized (Address.class) {
			System.out.println("Thread2: Holding lock on Address and Person");
		}
	}
}

Now when you run again the main class you will see the following output:

Thread2: Holding lock on Address
Thread2: Waiting for lock on Person
Thread2: Holding lock on Address and Person
Thread1: Holding lock on Person
Thread1: Waiting for lock on Address
Thread1: Holding lock on Person and Address

Now there is no deadlock because both run() methods in Thread1 and Thread2 are accessing lock on Address and Person classes in the same order. So, if thread 1 acquires a lock on Address object, thread 2 will not proceed until thread 1 releases Person lock, same way thread 1 will not be blocked even if thread 2 holds Person lock because now thread 2 will not expect thread 1 to release Address lock to proceed further.

Deadlock is the most common problem arises with a little bit of carelessness during multi-threading programming and it can completely stop the program.

Source Code

Download

Thanks for reading.

Leave a Comment