El punto muerto ocurre cuando los subprocesos (o lo que sea que su plataforma llame a sus unidades de ejecución) adquieren recursos, donde cada recurso solo puede ser retenido por un subproceso a la vez, y retiene esos recursos de tal manera que las retenciones no pueden ser reemplazadas, y Existe una relación "circular" entre los subprocesos de modo que cada subproceso en el punto muerto está esperando adquirir algún recurso en poder de otro subproceso.
Por lo tanto, una manera fácil de evitar el punto muerto es dar un orden total a los recursos e imponer una regla de que los recursos solo se adquieren mediante subprocesos en orden . Por el contrario, un interbloqueo se puede crear intencionalmente ejecutando subprocesos que adquieren recursos, pero no los adquieren en orden. Por ejemplo:
Dos hilos, dos cerraduras. El primer hilo ejecuta un bucle que intenta adquirir los bloqueos en un orden determinado, el segundo hilo ejecuta un bucle que intenta adquirir los bloqueos en el orden opuesto. Cada hilo libera ambos bloqueos después de adquirirlos con éxito.
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();
}
}
Ahora, ha habido algunos comentarios en esta pregunta que señalan la diferencia entre la probabilidad y la certeza del punto muerto. En cierto sentido, la distinción es una cuestión académica. Desde un punto de vista práctico, ciertamente me gustaría ver un sistema en ejecución que no se bloquee con el código que he escrito anteriormente :)
Sin embargo, las preguntas de la entrevista pueden ser académicas a veces, y esta pregunta SO tiene la palabra "seguramente" en el título, por lo que lo que sigue es un programa que ciertamente se bloquea. Se Locker
crean dos objetos, a cada uno se le asignan dos bloqueos y uno se CountDownLatch
utiliza para sincronizar entre los hilos. Cada uno Locker
bloquea el primer bloqueo y luego realiza una cuenta regresiva del pestillo una vez. Cuando ambos hilos han adquirido un bloqueo y han contado hacia atrás del pestillo, pasan la barrera del pestillo e intentan adquirir un segundo bloqueo, pero en cada caso el otro hilo ya mantiene el bloqueo deseado. Esta situación da como resultado un cierto punto muerto.
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();
}
}