Java Multithreading Tutorial With Example

How to detect and avoid deadlock in Java with example

This guide will help you understand the deadlock concept in Java. We will also take a look at ways to detect and avoid deadlock in Java using a few examples.

What you will learn:
– What is a deadlock in Java?
– Deadlock example
– How to detect a deadlock using an example.
– Best practice to avoid the deadlock in java.

A few more Multithreading Articles on Codedelay
Thread Basics
Race condition and Critical Section
What is a deadlock in Java?
What is ThreadLocal
ExecutorService with a few examples

What is Deadlock in Java

In the last tutorial, we have discussed race conditions and how to avoid race conditions using synchronization.

As we discussed we can use the synchronized method and block to lock a portion of the code.

In java, each object has a lock, and synchronization is a way to lock a method or code block so that at a time only one thread can access that method/block.

When a thread wants to execute a synchronized method it first tries to acquire the lock.

If another thread has already acquired the lock then this thread has to wait until another thread releases the lock.

Synchronization is good to prevent data inconsistency issues.

However, there is a problem with synchronization.

Suppose there are two threads ‘Thread-A’ and ‘Thread-B’.

Let’s assume that Thread-A has acquired the lock of Object-A and Thread-B has acquired the lock of Object-B.

Now assume that Thread-A which is executing Method-A wants to acquire the lock on Object-B however Thread-B already acquires the lock on Object-B.

Furthermore, Thread-B also wants to acquire the lock on Object-A, however, Thread-A has a lock on Object-A.

In this situation both threads Thread-A and Thread-B can’t finish their execution and will wait forever for the lock.

This condition is called as the Deadlock.

Deadlock in Java
Deadlock in Java

Let’s understand deadlock using a Java Example

Deadlock example in Java

package com.codedelay.concurrency;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo {

	private final Object object1 = new Object();
	private final Object object2 = new Object();

	public static void main(String[] args) {
		final DeadLockDemo deadLockDemo = new DeadLockDemo();
		Thread t1 = new Thread(() -> {
			while (true) {
				synchronized (deadLockDemo.object1) {
					try {
						System.out.println("Inside first synchronized block of first thread t1");
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (deadLockDemo.object2) {
						System.out.println("Inside second synchronized block of first thread t1");
					}
				}
			}
		});

		Thread t2 = new Thread(() -> {
			while (true) {
				synchronized (deadLockDemo.object2) {
					System.out.println("Inside first synchronized block of second thread t2");
					synchronized (deadLockDemo.object1) {
						System.out.println("Inside second synchronized block of second thread t2");
					}
				}
			}
		});
		
		t1.start();
		t2.start();
	}

}

In this example, we have created two threads t1 and t2.

t1 has acquired the lock of object1 and t2 has acquired the lock of the object2.

Now to process further t1 has to acquire the lock of object2.

Until t1 successfully gets object2 lock it will wait.

At the same time to process further t2 has to acquire the lock of object1.

And Until t2 successfully gets object1 lock it will wait.

This scenario is called a deadlock.

How to detect and avoid deadlock

Although it is not easy to detect and avoid deadlock compile time.

There are few ways to detect deadlock at runtime.

Let’s discuss a few ways to detect the deadlock.

Deadlock detection using ThreadMXBean

Once you already entered into deadlock condition then you can get further information about threads that are blocked due to deadlock.

Deadlock detection in Java using ThreadMXBean
Deadlock detection in Java using ThreadMXBean

ThreadMxBean is an interface in java.lang.management .

ThreadMxBean is a management interface of JVM’s thread system.

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
		long ids[] = bean.findMonitorDeadlockedThreads();
		ThreadInfo threadInfo[] = bean.getThreadInfo(ids);
		System.out.println(threadInfo.length);
		for (ThreadInfo info : threadInfo)
		{
			//returns the name of deadlocked thread
		    System.out.println(info.getThreadName());    
		}

As you could see in the above example we are calling findMonitorDeadlockedThreads from the ThreadMXBean instance.

findMonitorDeadlockedThreads returns threads information that are currently in deadlock (waiting to acquire locks).

findMonitorDeadlockedThreads
findMonitorDeadlockedThreads

Let me show you a complete example and output.

package com.codedelay.concurrency;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;

public class DeadLockDemo {

	private final Object object1 = new Object();
	private final Object object2 = new Object();

	public static void main(String[] args) {
		final DeadLockDemo deadLockDemo = new DeadLockDemo();
		Thread t1 = new Thread(() -> {
			while (true) {
				synchronized (deadLockDemo.object1) {
					try {
						System.out.println("Inside first synchronized block of first thread t1");
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (deadLockDemo.object2) {
						System.out.println("Inside second synchronized block of first thread t1");
					}
				}
			}
		});

		Thread t2 = new Thread(() -> {
			while (true) {
				synchronized (deadLockDemo.object2) {
					System.out.println("Inside first synchronized block of second thread t2");
					synchronized (deadLockDemo.object1) {
						System.out.println("Inside second synchronized block of second thread t2");
					}
				}
			}
		});

		Thread t3 = new Thread(() -> {
			System.out.println("Hello World");
			try {
				TimeUnit.SECONDS.sleep(6);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});

		t1.start();
		t2.start();
		t3.start();

		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(getThreadDump());
	}

	public static String getThreadDump() {
		final StringBuilder threadInfoStr = new StringBuilder();
		ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

		// long ids[] = threadMXBean.findMonitorDeadlockedThreads();
		// ThreadInfo threadInfo[] = threadMXBean.getThreadInfo(ids);

		final ThreadInfo[] threadInfo = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
		System.out.println(threadInfo.length);
		for (ThreadInfo info : threadInfo) {
			threadInfoStr.append('"');
			threadInfoStr.append(info.getThreadName());
			threadInfoStr.append("\" ");
			final Thread.State state = info.getThreadState();
			threadInfoStr.append("\n   java.lang.Thread.State: ");
			threadInfoStr.append(state);
			final StackTraceElement[] stackTraceElements = info.getStackTrace();
			for (final StackTraceElement stackTraceElement : stackTraceElements) {
				threadInfoStr.append("\n        at ");
				threadInfoStr.append(stackTraceElement);
			}
			threadInfoStr.append("\n\n");
		}
		return threadInfoStr.toString();
	}

}

Output of the above program would be

"main" 
   java.lang.Thread.State: RUNNABLE
        at java.management@12/sun.management.ThreadImpl.getThreadInfo1(Native Method)
        at java.management@12/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:190)
        at app//com.codedelay.concurrency.DeadLockDemo.getThreadDump(DeadLockDemo.java:70)
        at app//com.codedelay.concurrency.DeadLockDemo.main(DeadLockDemo.java:60)

"Reference Handler" 
   java.lang.Thread.State: RUNNABLE
        at java.base@12/java.lang.ref.Reference.waitForReferencePendingList(Native Method)
        at java.base@12/java.lang.ref.Reference.processPendingReferences(Reference.java:241)
        at java.base@12/java.lang.ref.Reference$ReferenceHandler.run(Reference.java:213)

"Finalizer" 
   java.lang.Thread.State: WAITING
        at java.base@12/java.lang.Object.wait(Native Method)
        at java.base@12/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155)
        at java.base@12/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:176)
        at java.base@12/java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:170)

"Signal Dispatcher" 
   java.lang.Thread.State: RUNNABLE

"Attach Listener" 
   java.lang.Thread.State: RUNNABLE

"Common-Cleaner" 
   java.lang.Thread.State: TIMED_WAITING
        at java.base@12/java.lang.Object.wait(Native Method)
        at java.base@12/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155)
        at java.base@12/jdk.internal.ref.CleanerImpl.run(CleanerImpl.java:148)
        at java.base@12/java.lang.Thread.run(Thread.java:835)
        at java.base@12/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:134)

"Thread-0" 
   java.lang.Thread.State: BLOCKED
        at app//com.codedelay.concurrency.DeadLockDemo.lambda$0(DeadLockDemo.java:25)
        at app//com.codedelay.concurrency.DeadLockDemo$$Lambda$1/0x0000000801200840.run(Unknown Source)
        at java.base@12/java.lang.Thread.run(Thread.java:835)

"Thread-1" 
   java.lang.Thread.State: BLOCKED
        at app//com.codedelay.concurrency.DeadLockDemo.lambda$1(DeadLockDemo.java:36)
        at app//com.codedelay.concurrency.DeadLockDemo$$Lambda$2/0x0000000801200c40.run(Unknown Source)
        at java.base@12/java.lang.Thread.run(Thread.java:835)

"Thread-2" 
   java.lang.Thread.State: TIMED_WAITING
        at java.base@12/java.lang.Thread.sleep(Native Method)
        at java.base@12/java.lang.Thread.sleep(Thread.java:340)
        at java.base@12/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
        at app//com.codedelay.concurrency.DeadLockDemo.lambda$2(DeadLockDemo.java:45)
        at app//com.codedelay.concurrency.DeadLockDemo$$Lambda$3/0x0000000801201040.run(Unknown Source)
        at java.base@12/java.lang.Thread.run(Thread.java:835)

How to avoid Deadlock

Deadlocks are bad.

Your complete application stops working once it faces a deadlock condition.

Moreover, when you get deadlock condition in a production application, then it is not just hard to troubleshoot but also very difficult to fix them.

However, you can avoid deadlocks by taking care of a few steps.

  1. Try to avoid nested synchronization blocks.
    In nested synchronization block one thread try to acquire another lock when it is already holding one lock.
  2. Lock ordering: If you can’t avoid nested synchronization block then you should make sure that threads will acquire locks in the same order. In the above example, one thread firstly acquired the lock on object1 and then tried to acquire an object2 lock.
    However, thread t2 acquired object2 lock first then tried to acquire the lock of object1.
    Hence deadlock occurred.
  3. Lock Timeout: You can also specify a timeout.
    If a thread fails to acquire a lock then it should wait for a specific time before retry to acquire the lock.

Conclusion

In this tutorial, we discussed Deadlock in Java, how to detect deadlock and how to avoid them with a few examples.

Click to rate this post!
[Total: 0 Average: 0]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.