In a multi-threaded environment there may come a situation when one thread is waiting for a resource that is locked by another thread, which in turn is waiting for another thread and so on until this dependency loops back to the first waiting thread. Thus all the threads are waiting for each other to release the resources to make any further progress and blocked forever in the process. This scenario is called deadlock in multi-threading.
Deadlock example
To explain deadlock in Java we can take a simple scenario of two threads Thread1 and Thread2, where Thread1 holds a lock on obj1 and waiting to acquire a lock on obj2. At the same time Thread2 has a lock on obj2 and waiting to acquire lock on obj1. Here both threads are blocked in a circular loop where Thread1 is waiting to acquire lock on obj2 and Thread2 is waiting to acquire lock on obj1 thus creating a deadlock.
Deadlock in Java scenarios
You may get deadlock in Java because of the improper use of the synchronized keyword. Scenarios when deadlock may happen are as follows.
- Nested synchronized blocks with reverse ordering of objects.
- Calling one synchronized method from another where methods are not using the same object to synchronize.
Deadlock in Java example
First example shows the scenario when there are nested synchronized blocks with reverse ordering of objects.
class ThreadA implements Runnable{ private Object obj1; private Object obj2; ThreadA(Object obj1, Object obj2){ this.obj1 = obj1; this.obj2 = obj2; } @Override public void run() { synchronized(obj1){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj1 lock"); synchronized(obj2){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj2 lock"); } } } } class ThreadB implements Runnable{ private Object obj1; private Object obj2; ThreadB(Object obj1, Object obj2){ this.obj1 = obj1; this.obj2 = obj2; } @Override public void run() { synchronized(obj2){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj2 lock"); synchronized(obj1){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj1 lock"); } } } } public class DLDemo { public static void main(String[] args) { Object obj1 = new Object(); Object obj2 = new Object(); Thread t1 = new Thread(new ThreadA(obj1, obj2)); Thread t2 = new Thread(new ThreadB(obj1, obj2)); t1.start(); t2.start(); } }Output
Thread-0 acquired obj1 lock Thread-1 acquired obj2 lock
In ThreadA’s run method synchronization is first done on obj1 and later on obj2. In ThreadB’s run method synchronization is reversed, it is first done on obj2 and later on obj1. That may result in a deadlock where t1 gets a lock on obj1 and waiting to acquire a lock on obj2. At the same time obj2 has acquired a lock on obj2 and waiting to acquired a lock on obj1.
Another deadlock Java example shows the scenario where one synchronized method is called from another.
public class DLDemo { public synchronized void method1(DLDemo obj){ System.out.println(Thread.currentThread().getName() + " In Method1"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //Calling another synchronized method obj.method2(this); } public synchronized void method2(DLDemo obj2){ System.out.println("In Method2"); } public static void main(String[] args) { DLDemo obj1 = new DLDemo(); DLDemo obj2 = new DLDemo(); new Thread(new Runnable() { public void run() { obj1.method1(obj2); } }).start(); //Thread 2 new Thread(new Runnable() { public void run() { obj2.method1(obj1); } }).start(); } }Output
Thread-0 In Method1 Thread-1 In Method1
In the code there are two instances of DLDemo class, one thread calls the synchronized method method1 using obj1 and another thread calls it using obj2. Which means Thread1 holds a lock on obj1 and Thread2 holds a lock on obj2.
With in the synchronized method method1 there is a call to another synchronized method method2, both of the threads are trying to call method2 with the object whose lock is held by another object thus leading to a deadlock.
How to avoid deadlock in Java
With multi-threading programming deadlock may occur and there is no language support as such to prevent deadlocks. You will have to write your code used by multiple threads carefully to avoid deadlocks. Here we’ll go through the scenarios shown above and see how deadlocks can be avoided in those scenarios.
1. As shown in the previous scenarios one of the reason for the deadlock in Java is the way locks are acquired, if you have nested synchronization then you can acquire the object locks in the same order rather than in reverse order for both threads.
Changed nested synchronization code
class ThreadA implements Runnable{ private Object obj1; private Object obj2; ThreadA(Object obj1, Object obj2){ this.obj1 = obj1; this.obj2 = obj2; } @Override public void run() { synchronized(obj1){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj1 lock"); synchronized(obj2){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj2 lock"); } } } } class ThreadB implements Runnable{ private Object obj1; private Object obj2; ThreadB(Object obj1, Object obj2){ this.obj1 = obj1; this.obj2 = obj2; } @Override public void run() { synchronized(obj1){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj1 lock"); synchronized(obj2){ System.out.println(Thread.currentThread().getName() + " acquired " + "obj2 lock"); } } } }Output
Thread-0 acquired obj1 lock Thread-0 acquired obj2 lock Thread-1 acquired obj1 lock Thread-1 acquired obj2 lock
As you can see from the output now the deadlock is avoided.
2. Using synchronized block to minimize the synchronization to the critical section code only will also help in avoiding the deadlock in Java.
In the second scenario rather than synchronizing the whole method synchronized block can be used.
public class DLDemo { public void method1(DLDemo obj){ System.out.println(Thread.currentThread().getName() + " In Method1"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized(this){ //Calling another synchronized method obj.method2(this); } } public void method2(DLDemo obj2){ System.out.println("In Method2"); synchronized(this){ System.out.println("In Method2 synchronized block"); } } public static void main(String[] args) { DLDemo obj1 = new DLDemo(); DLDemo obj2 = new DLDemo(); new Thread(new Runnable() { public void run() { obj1.method1(obj2); } }).start(); //Thread 2 new Thread(new Runnable() { public void run() { obj2.method1(obj1); } }).start(); } }
3- By using static synchronization in Java. If two object instances are used two threads using two separate objects can still enter a synchronized method or block with their separate object locks. Static synchronization helps in this case because the lock will be acquired at the class level then.
How to debug a deadlock in Java
Detecting a deadlock in Java is not easy, even logs may be of little help. If you observe that your multi-threaded code is not performing as well as it was then it may be due to a deadlock and best thing is to get a thread dump of the application and analyze it.
You can use jstack utility to get a thread dump by providing the pid of the Java application. That pid can be obtained by running jps command. For example, if I run the program where deadlock was created due to nested synchronization, then I can get the thread dump using following steps.
1- By using jps command I can get the pid of the Java application.
Jps 5968 7408 DLDemo 13960 Jps
2- Run the jstack command with the pid as argument.
Jstack 7408
3- Get the thread dump and analyze it. Here some of the relevant portion of the thread dump is displayed.
"Thread-1" #11 prio=5 os_prio=0 tid=0x000000001b3e1000 nid=0x145c waiting for monitor entry [0x000000001bade000] java.lang.Thread.State: BLOCKED (on object monitor) at com.knpcode.ThreadB.run(DLDemo.java:33) - waiting to lock <0x00000000d5bfaff0> (a java.lang.Object) - locked <0x00000000d5bfb000> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) "Thread-0" #10 prio=5 os_prio=0 tid=0x000000001b3e0000 nid=0x379c waiting for monitor entry [0x000000001b9de000] java.lang.Thread.State: BLOCKED (on object monitor) at com.knpcode.ThreadA.run(DLDemo.java:15) - waiting to lock <0x00000000d5bfb000> (a java.lang.Object) - locked <0x00000000d5bfaff0> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000004d4c800 nid=0x2b34 in Object.wait() [0x000000001acee000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000d5b88ec0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(Unknown Source) - locked <0x00000000d5b88ec0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000004d42000 nid=0x6cc in Object.wait() [0x000000001abef000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000d5b86b68> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Unknown Source) at java.lang.ref.Reference.tryHandlePending(Unknown Source) - locked <0x00000000d5b86b68> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source) Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x0000000004d47868 (object 0x00000000d5bfaff0, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x0000000004d4a0f8 (object 0x00000000d5bfb000, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at com.knpcode.ThreadB.run(DLDemo.java:33) - waiting to lock <0x00000000d5bfaff0> (a java.lang.Object) - locked <0x00000000d5bfb000> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) "Thread-0": at com.knpcode.ThreadA.run(DLDemo.java:15) - waiting to lock <0x00000000d5bfb000> (a java.lang.Object) - locked <0x00000000d5bfaff0> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) Found 1 deadlock.
That's all for the topic Deadlock in Java With Examples. If something is missing or you have something to share about the topic please write a comment.
You may also like
- Race Condition in Java With Examples
- Producer-Consumer Problem Java Program
- finalize() Method in Java
- Java try-with-resources With Examples
- Java ArrayList With Examples
- Java Program to Find Maximum And Minimum Number in a Matrix
- Java Program to Compress File in gzip Format in Hadoop
- Spring @PostConstruct and @PreDestroy Annotation
- React Router - Link in react-router-dom
- React Form + Formik + Yup Validation Example
No comments:
Post a Comment