Deadlock concept in Java

Asked

Viewed 863 times

6

I came across the term deadlock in Java. But, I can’t understand it. So I would like to ask these questions:

  • What is a deadlock?
  • Why and how to avoid a deadlock?
  • 1

    Related: https://answall.com/q/283375/64969

1 answer

6


Deadlock is the cyclical dependence that happens between two or more threads sharing two or more resources (variable, code block, etc.). In other words, a thread T1 depends on another T2, whereas this T2 depends, in turn, on T1.

This type of dependency happens exclusively by failure in the logic implemented by the programmer, who, when developing an application multithreaded, need to worry not only about deadlock, but with other concepts relating to concurrent programming, such as starvation, livelock and race condition, the latter being probably the most common to happen.

An example of code in which occurs deadlock:

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();

   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }

   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");

            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");

            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

When the application is executed and both threads initiated, T1 acquires the lock (as if it were an access exclusivity), represented by the word synchronized, about the object Lock1. I mean, until T1 release the lock on this object (that is, until the code block within synchronized is completely executed), no other thread can access it. One can notice that within this first block synchronized, there is another attempt to lock (represented by the other block synchronized), this time on Lock2. That means, changed as kids, that the lock original on the object Lock1 will only be released if the thread T1 also achieve the lock about Lock2 and execute the code within that second block. Summarizing, at this point in the code T1 has lock about Lock1 and PRECISE of lock about Lock2 to end your routine and release all resources to other threads.

In turn, T2, when it is started, acquires the lock about Lock2 and, to finalize its execution and release the resources, PRECISE of lock about Lock1.

With this panorama, it is easy to understand what is happening: a thread T1 lockou the object Lock1 and must win the lock about Lock2 to finalize, but T2 lockou Lock2 and to finish you must win the lock about Lock1, which in turn is locked by T1. This is cyclical dependence, or deadlock. The output of that code when executed:

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

At this point, your application is in a permanent state of waiting and so it will stay forever and that is why you never want a deadlock.

And here comes the question: how to correct the code so that it does not occur? Just reverse the order of the objects from which you want the lock. In other words: both threads will first try to lock the object Lock1 (and, succeeding, also lockar Lock2), execute the code and finish, allowing then the next thread acquire the lock and also run your due code:

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();

   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }

   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");

            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Executing, this is the output:

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

And your application will be ready to continue running whatever comes next.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.