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.