Si verifica un deadlock quando i thread (o qualunque cosa la tua piattaforma chiami le sue unità di esecuzione) acquisiscono risorse, dove ogni risorsa può essere trattenuta solo da un thread alla volta e mantiene tali risorse in modo tale che le conservazioni non possono essere anticipate e esiste una relazione "circolare" tra i thread in modo che ogni thread nel deadlock sia in attesa di acquisire una risorsa contenuta da un altro thread.
Quindi, un modo semplice per evitare il deadlock è dare un ordine totale alle risorse e imporre una regola secondo cui le risorse vengono acquisite solo dai thread in ordine . Al contrario, un deadlock può essere creato intenzionalmente eseguendo thread che acquisiscono risorse, ma non le acquisiscono in ordine. Per esempio:
Due fili, due serrature. Il primo thread esegue un ciclo che tenta di acquisire i blocchi in un certo ordine, il secondo thread esegue un ciclo che tenta di acquisire i blocchi nell'ordine opposto. Ogni thread rilascia entrambi i blocchi dopo aver acquisito correttamente i blocchi.
public class HighlyLikelyDeadlock {
static class Locker implements Runnable {
private Object first, second;
Locker(Object first, Object second) {
this.first = first;
this.second = second;
}
@Override
public void run() {
while (true) {
synchronized (first) {
synchronized (second) {
System.out.println(Thread.currentThread().getName());
}
}
}
}
}
public static void main(final String... args) {
Object lock1 = new Object(), lock2 = new Object();
new Thread(new Locker(lock1, lock2), "Thread 1").start();
new Thread(new Locker(lock2, lock1), "Thread 2").start();
}
}
Ora, ci sono stati alcuni commenti in questa domanda che sottolineano la differenza tra la probabilità e la certezza dello stallo. In un certo senso, la distinzione è una questione accademica. Da un punto di vista pratico, mi piacerebbe sicuramente vedere un sistema in esecuzione che non si blocchi con il codice che ho scritto sopra :)
Tuttavia, le domande dell'intervista a volte possono essere accademiche, e questa domanda SO ha la parola "sicuramente" nel titolo, quindi quello che segue è un programma che certamente si blocca. Locker
Vengono creati due oggetti, a ciascuno vengono assegnati due blocchi e uno CountDownLatch
utilizzato per la sincronizzazione tra i thread. Ciascuno Locker
blocca il primo blocco, quindi conta alla rovescia una volta. Quando entrambi i thread hanno acquisito un blocco e hanno contato alla rovescia il blocco, procedono oltre la barriera del blocco e tentano di acquisire un secondo blocco, ma in ogni caso l'altro thread contiene già il blocco desiderato. Questa situazione si traduce in un certo deadlock.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CertainDeadlock {
static class Locker implements Runnable {
private CountDownLatch latch;
private Lock first, second;
Locker(CountDownLatch latch, Lock first, Lock second) {
this.latch = latch;
this.first = first;
this.second = second;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
first.lock();
latch.countDown();
System.out.println(threadName + ": locked first lock");
latch.await();
System.out.println(threadName + ": attempting to lock second lock");
second.lock();
System.out.println(threadName + ": never reached");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(final String... args) {
CountDownLatch latch = new CountDownLatch(2);
Lock lock1 = new ReentrantLock(), lock2 = new ReentrantLock();
new Thread(new Locker(latch, lock1, lock2), "Thread 1").start();
new Thread(new Locker(latch, lock2, lock1), "Thread 2").start();
}
}