Saturday, 4 November 2017

Deadlock in Java Multithreading: Program to Create and Avoid Deadlock

           This is a very important interview question, especially from a Java multithreading perspective. First, let us understand what a deadlock is and how to create a deadlock by writing a simple Java program. In this post, we will learn about deadlocks, how to reproduce them, and how to resolve them.

The synchronized keyword is used to make a class or method thread-safe. It ensures that only one thread can acquire the lock of a synchronized method or block and execute it at a time. Other threads must wait until the lock is released and one of them acquires it.

The synchronized keyword is important when an application runs in a multi-threaded environment where two or more threads execute concurrently. It helps prevent data inconsistency by controlling access to shared resources.


What is a Deadlock ?


           In Java multithreading, a deadlock is a situation where two or more threads hold locks on different resources and wait indefinitely for each other to release the locks. Since each thread is waiting for a resource held by another thread, none of them can proceed, and they remain blocked forever.

Below is a simple example demonstrating a deadlock:

Deadlock in Java
Deadlock example


















Program to generate the Deadlock :


package com.pr;

public class DeadlockEx {
 
      public static Object object1 = new Object();
      public static Object object2 = new Object();
      public static void main(String[] args) {
              ThreadEx1 thread1 = new ThreadEx1();
              ThreadEx2 thread2 = new ThreadEx2();
              thread1.start();
              thread2.start();
      }
 
      private static class ThreadEx1 extends Thread {
             public void run() {
                    synchronized(object1) {
                         System.out.println("ThreadEx1 : holding lock on object1");
                         try {
                                Thread.sleep(1000);
                          } catch (InterruptedException e) {
                             // TODO Auto-generated catch block
                          }
                          System.out.println("ThreadEx1 : waitig to release lock on object2");
                          synchronized(object2) {
                                  System.out.println("ThreadEx1: both object1 and object2 in lock");
                          }
                   }
            }
      }
 
      private static class ThreadEx2 extends Thread {
              public void run() {
                     synchronized(object2) {
                             System.out.println("ThreadEx2 : holding lock on object2");
                             try {
                                   Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                    // TODO Auto-generated catch block
                              }
                              System.out.println("ThreadEx2 : waitig to release lock on object1");
                              synchronized(object1) {
                                    System.out.println("ThreadEx2: both object1 and object2 in lock");
                              }
                     }
              }
      }

}
   
Output:- ThreadEx1 : holding lock on object1
                ThreadEx2 : holding lock on object2
                ThreadEx2 : waitig to release lock on object1
                ThreadEx1 : waitig to release lock on object2


The above program will hang forever because neither of thread to proceed to execute the task and not to release the lock .

Deadlock Solution:

  
         In the above code, we have simply changed the order in which locks are acquired and observed the output. Changing the lock acquisition order can help avoid the deadlock situation.

This is a simple example demonstrating the deadlock problem and its solution. However, in real-time applications, identifying and resolving deadlock issues can be much more challenging because of the complexity of the code and the number of resources involved.

The deadlock solution program is shown below:

package com.pr;

public class DeadlockEx {
 
      public static Object object1 = new Object();
      public static Object object2 = new Object();
      public static void main(String[] args) {
              ThreadEx1 thread1 = new ThreadEx1();
              ThreadEx2 thread2 = new ThreadEx2();
              thread1.start();
              thread2.start();
      }
 
      private static class ThreadEx1 extends Thread {
             public void run() {
                    synchronized(object1) {
                             System.out.println("ThreadEx1 : holding lock on object1");
                             try {
                                   Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  // TODO Auto-generated catch block
                              }
                              System.out.println("ThreadEx1 : waiting to release lock on object2");
                              synchronized(object2) {
                                     System.out.println("ThreadEx1: both object1 and object2 in lock");
                              }
                      }
             }
      }
 
      private static class ThreadEx2 extends Thread {
             public void run() {
                   synchronized(object1) {
                         System.out.println("ThreadEx2 : holding lock on object1");
                         try {
                                Thread.sleep(1000);
                         } catch (InterruptedException e) {
                               // TODO Auto-generated catch block
                         }
                         System.out.println("ThreadEx2 : waiting to release lock on object2");
                         synchronized(object2) {
                                System.out.println("ThreadEx2: both object1 and object2 in lock");
                         }
                   }
             }
      }

}

Output :- ThreadEx1 : holding lock on object1
                 ThreadEx1 : waiting to release lock on object2
                 ThreadEx1: both object1 and object2 in lock
                 ThreadEx2 : holding lock on object1
                 ThreadEx2 : waiting to release lock on object2
                 ThreadEx2: both object1 and object2 in lock




Important Points to Avoid Deadlock:

  1. Lock specific member variables of a class instead of locking the entire class. This helps reduce lock contention and improves application performance.

  2. Use the join() method when appropriate. The advantage of using join() is that it allows one thread to wait for another thread to complete its execution before continuing, ensuring a sequential execution flow where required.                   

VisualVM and jstack are useful tools for detecting deadlocks in Java applications. For more details about VisualVM and jstack, refer to the resources Java thread dump.


Related Posts: 

No comments:

Post a Comment