Red de conocimiento informático - Material del sitio web - Cómo entender el punto muerto en Java

Cómo entender el punto muerto en Java

Un punto muerto ocurre cuando se bloquean varios subprocesos al mismo tiempo, con uno o todos los subprocesos esperando que se libere algún recurso. Debido a que el hilo está bloqueado indefinidamente, el programa no puede finalizar normalmente.

La causa principal de los interbloqueos es el uso inadecuado de la palabra clave "sincronizado" para gestionar el acceso de subprocesos a objetos específicos. La función de la palabra clave sincronizada es garantizar que solo un subproceso pueda ejecutar un bloque de código específico en un momento dado, por lo que el subproceso al que se le permite ejecutar primero debe tener acceso exclusivo a la variable u objeto cuando accede un subproceso. un objeto, el hilo bloqueará el objeto y el bloqueo hará que otros hilos que también quieran acceder al mismo objeto sean bloqueados hasta que el primer hilo libere el bloqueo que mantiene sobre el objeto.

Por lo tanto. , cuando se utiliza la palabra clave " sincronizada ", es fácil que dos subprocesos esperen entre sí para realizar ciertas operaciones. El Código 1 es un ejemplo simple de interbloqueo.

//Código 1

clase Deadlocker {

int campo_1;

bloqueo de objeto privado_1 = nuevo int[1];

int campo_2;

bloqueo de objeto privado_2 = new int[1];

public void método1(valor int) {

"sincronizado" (lock_1) {

" sincronizado" (lock_2) {

campo_1 = 0; campo_2 = 0;

}

}

}

método público vacío2( valor int) {

"sincronizado" (lock_2) {

"sincronizado" (lock_1) {

campo_1 = 0 campo_2 = 0;

}

}

}

Consulte el código 1 y considere el siguiente proceso:

◆ Un hilo (ThreadA ) llama al método1()

◆ El subproceso A está sincronizado en lock_1, pero se permite la preferencia

◆ Otro subproceso (ThreadB) inicia la ejecución. Subproceso B. Llame al método 2().

◆ El subproceso B adquiere lock_2 y continúa la ejecución, intentando adquirir lock_1. Sin embargo, el subproceso B no puede adquirir lock_1 porque el subproceso A posee lock_1. El subproceso B se está bloqueando porque está esperando que el subproceso A libere lock_1. Ahora es el turno del subproceso A de continuar la ejecución. El subproceso A intenta adquirir lock_2, pero lock_2 ya es propiedad del subproceso B. No se puede obtener.

◆ Tanto el hilo A como el hilo B están bloqueados y el programa cae en un punto muerto.

Por supuesto, la mayoría de los puntos muertos no serán tan obvios y requerirán un análisis cuidadoso del código para encontrarlos, especialmente para programas multiproceso más grandes. Una buena herramienta de análisis de subprocesos, como JProbe Threadalyzer, puede analizar interbloqueos e identificar dónde ocurre el problema en el código.

Interbloqueos implícitos

Los interbloqueos implícitos son causados ​​por prácticas de programación deficientes, pero no necesariamente ocurren cada vez que se ejecuta una prueba. Por lo tanto, es posible que algunos bloqueos implícitos no se descubran hasta después del lanzamiento oficial de la aplicación, lo que los hace más dañinos que los bloqueos normales. Las siguientes son dos situaciones que conducen a interbloqueos implícitos: orden de bloqueo, posesión y espera.

Orden de bloqueo

Los conflictos de orden de bloqueo ocurren cuando varios subprocesos simultáneos intentan ocupar dos bloqueos al mismo tiempo. Si un hilo mantiene un bloqueo que otro hilo requiere, puede ocurrir un punto muerto.

Considere la siguiente situación: el subproceso A y el subproceso B deben poseer lock_1 y lock_2 al mismo tiempo. El proceso de bloqueo es el siguiente:

◆ El subproceso A obtiene lock_1

◆. Se adelanta el subproceso A, el programador de la máquina virtual se transfiere al subproceso B;

◆ El subproceso B obtiene lock_2

◆ Se adelanta el subproceso B y el programador de la máquina virtual se transfiere al subproceso A;

◆ El subproceso A intenta adquirir lock_2, pero lock_2 está ocupado por el subproceso B, por lo que el subproceso A está bloqueado

◆ El programador va al subproceso B

◆ El subproceso B intenta adquirir lock_1, pero lock_1 está ocupado por el subproceso A, por lo que el subproceso B se bloquea

◆ El subproceso A y el subproceso B se bloquean.

Cabe señalar que a veces el proceso de interbloqueo anterior no ocurrirá y el código no cambiará en absoluto. El programador de la máquina virtual puede permitir que uno de los subprocesos adquiera lock_1 y lock_2 al mismo tiempo. es decir, thread El proceso de adquisición de ambos candados es ininterrumpido. En este caso, la detección tradicional de interbloqueos tiene dificultades para determinar dónde reside el error.

Poseer y esperar

Otro tipo de punto muerto implícito puede ocurrir si un subproceso adquiere el bloqueo y luego debe esperar la notificación de otro subproceso; consulte el código dos.

//Código dos

cola de clase pública {

estática java.lang.Object queueLock_;

Productor productor_;

Consumidor consumidor_;

Productor de clase pública {

void producir() {

while (!done) {

"sincronizado" (queueLock_) {

produceItemAndAddItToQueue();

"sincronizado" (consumer_) {

consumer_.notify();

}

}

}

}

clase pública Consumidor {

consumir() {

while (!done) {

"sincronizado" (queueLock_) {

"sincronizado" (consumidor_) {

consumidor_. ();

}

removeItemFromQueueAndProcessIt();

}

}

}

}

}

}

}

}

}

}

En el código dos, el productor notifica al consumidor después de agregar un nuevo elemento a la cola para que pueda procesar el nuevo contenido. El problema es que el consumidor puede mantener un bloqueo agregado a la cola, lo que impide que el productor acceda a la cola, y continuar manteniendo el bloqueo incluso mientras el consumidor espera una notificación del productor. Esto provocará un punto muerto porque el productor no puede agregar contenido nuevo a la cola mientras el consumidor espera a que se le notifique al productor que se ha agregado contenido nuevo.

El bloqueo mantenido durante el proceso de espera es un punto muerto implícito, porque las cosas pueden desarrollarse de acuerdo con una situación más ideal: el hilo productor no necesita el bloqueo mantenido por el consumidor. Aún así, este estilo de programación no es seguro a menos que exista una razón absolutamente sólida para garantizar que el subproceso productor nunca necesite el bloqueo. A veces, "mantener y esperar" puede resultar en una cadena de subprocesos en espera, por ejemplo, si el subproceso A tiene un candado que el subproceso B necesita y espera, y el subproceso B mantiene un candado que el subproceso C necesita y espera, y así sucesivamente.

Para corregir el error en el Código 2, simplemente modifique la clase Consumidor para mover wait() fuera de "sincronizado"().