Simultaneidade Java - Impasse

Deadlock descreve uma situação em que dois ou mais threads são bloqueados para sempre, esperando um pelo outro. O deadlock ocorre quando vários threads precisam dos mesmos bloqueios, mas os obtêm em ordem diferente. Um programa Java multithread pode sofrer com a condição de deadlock porque osynchronizedpalavra-chave faz com que o encadeamento em execução bloqueie enquanto espera pelo bloqueio ou monitor associado ao objeto especificado. Aqui está um exemplo.

Exemplo

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...");
            }
         }
      }
   } 
}

Quando você compila e executa o programa acima, você encontra uma situação de impasse e a seguir está a saída produzida pelo programa -

Resultado

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

O programa acima irá travar para sempre porque nenhum dos threads está em posição de prosseguir e aguardando um ao outro para liberar o bloqueio, então você pode sair do programa pressionando CTRL + C.

Exemplo de solução de deadlock

Vamos mudar a ordem de bloqueio e execução do mesmo programa para ver se ambos os threads ainda esperam um pelo outro -

Exemplo

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...");
            }
         }
      }
   } 
}

Portanto, apenas alterar a ordem dos bloqueios evita que o programa entre em uma situação de impasse e conclui com o seguinte resultado -

Resultado

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...

O exemplo acima é apenas para deixar o conceito claro, no entanto, é um conceito complexo e você deve mergulhar fundo nele antes de desenvolver seus aplicativos para lidar com situações de deadlock.