O multi-threading é uma técnica que ajuda a melhorar o desempenho de um aplicativo, mas também pode apresentar alguns problemas. Neste tutorial, vamos analisar dois desses problemas, deadlock e livelock, com a ajuda de exemplos em Java.
O que é Deadlock? Um deadlock ocorre quando dois ou mais threads esperam indefinidamente por um bloqueio ou recurso mantido por outro thread. Como resultado, um aplicativo pode paralisar ou falhar, já que os threads deadlocked não podem progredir.
Exemplo de Deadlock Vamos dar uma olhada em um exemplo simples em Java para entender o deadlock.
Neste exemplo, criaremos dois threads, T1 e T2. O thread T1 chama operation1 e o thread T2 chama operation2.
Para concluir suas operações, o thread T1 precisa adquirir lock1 primeiro e depois lock2, enquanto o thread T2 precisa adquirir lock2 primeiro e depois lock1. Então, basicamente, ambos os threads estão tentando adquirir os bloqueios na ordem oposta.
Agora, vamos escrever a classe DeadlockExample:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockExample {
private Lock lock1 = new ReentrantLock(true);
private Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
DeadlockExample deadlock = new DeadlockExample();
new Thread(deadlock::operation1, "T1").start();
new Thread(deadlock::operation2, "T2").start();
}
public void operation1() {
lock1.lock();
System.out.println("lock1 acquired, waiting to acquire lock2.");
sleep(50);
lock2.lock();
System.out.println("lock2 acquired.");
System.out.println("executing first operation.");
lock2.unlock();
lock1.unlock();
}
public void operation2() {
lock2.lock();
System.out.println("lock2 acquired, waiting to acquire lock1.");
sleep(50);
lock1.lock();
System.out.println("lock1 acquired.");
System.out.println("executing second operation.");
lock1.unlock();
lock2.unlock();
}
// helper methods
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Ao executar este exemplo de deadlock, podemos observar a saÃda:
lock1 acquired, waiting to acquire lock2.
lock2 acquired, waiting to acquire lock1.
O Deadlock é um problema comum de concorrência em Java. Portanto, devemos projetar um aplicativo Java para evitar quaisquer condições de deadlock potenciais. Para começar, devemos evitar a necessidade de adquirir vários bloqueios para um thread. No entanto, se um thread precisar de vários bloqueios, devemos garantir que cada thread adquira os bloqueios na mesma ordem, para evitar qualquer dependência cÃclica na aquisição de bloqueios. Também podemos usar tentativas de bloqueio cronometradas, como o método tryLock na interface Lock, para garantir que um thread não bloqueie indefinidamente se não puder adquirir um bloqueio.