await - ''Reentrancy'' en java
lock en java (4)
La reentrada significa que los bloqueos se adquieren por subproceso en lugar de por invocación.
Como un hilo mantiene un bloqueo intrínseco, ¿no significa que un hilo ejecutado una vez es igual a una invocación?
Gracias, parece que significa que: en un hilo, si obtengo un bloqueo lockA
cuando la función de proceso doA
que llama a la función doB
, y doB
también necesita un lock lockA
, entonces habrá una reentrada. En Java, este fenómeno se adquiere por hilo, así que no necesito considerar los bloqueos.
La reentrada significa que los bloqueos se adquieren por subproceso en lugar de por invocación.
Esa es una definición engañosa. Es cierto (más o menos), pero pasa por alto el punto real.
Reentrada significa (en general, terminología de CS / IT) que haces algo, y mientras lo sigues haciendo, lo haces de nuevo. En el caso de los bloqueos, significa que debes hacer algo como esto en un solo hilo :
- Adquiera un bloqueo en "foo".
- Hacer algo
- Adquiera un bloqueo en "foo". Tenga en cuenta que no hemos liberado el bloqueo que adquirimos previamente.
- ...
- Bloqueo de liberación en "foo"
- ...
- Bloqueo de liberación en "foo"
Con un mecanismo de bloqueo / bloqueo reentrante, el intento de adquirir el mismo bloqueo tendrá éxito, y aumentará un contador interno que pertenece al bloqueo. El bloqueo solo se liberará cuando el titular actual del bloqueo lo haya liberado dos veces.
Aquí hay un ejemplo en Java usando bloqueos / monitores de objetos primitivos ... que son reentrantes:
Object lock = new Object();
...
synchronized (lock) {
...
doSomething(lock, ...)
...
}
public void doSomething(Object lock, ...) {
synchronized (lock) {
...
}
}
La alternativa a la reentrada es el bloqueo no reentrante, donde sería un error que un hilo intente adquirir un bloqueo que ya tiene.
La ventaja de utilizar bloqueos de reentrada es que no tiene que preocuparse por la posibilidad de fallar debido a la adquisición accidental de un bloqueo que ya tiene. La desventaja es que no puede suponer que nada de lo que llame cambiará el estado de las variables que el bloqueo está diseñado para proteger. Sin embargo, eso no suele ser un problema. Los bloqueos generalmente se utilizan para proteger contra cambios de estado concurrentes realizados por otros hilos.
Entonces, ¿no necesito considerar bloqueos?
Si tu puedes.
Un hilo no se estancará contra sí mismo (si el bloqueo es reentrante). Sin embargo, puede obtener un interbloqueo si hay otros subprocesos que pueden tener un bloqueo en el objeto que está intentando bloquear.
Esto solo significa que una vez que un hilo tiene un bloqueo, puede ingresar a la sección bloqueada del código tantas veces como sea necesario. Entonces, si tiene una sección sincronizada de código, como un método, solo el hilo que logró el bloqueo puede llamar a ese método, pero puede llamar a ese método tantas veces como lo desee, incluido cualquier otro código mantenido por el mismo candado. Esto es importante si tiene un método que llama a otro método, y ambos están sincronizados por el mismo bloqueo. Si este no fue el caso, el. La segunda llamada al método bloquearía. También se aplicaría a las llamadas a métodos recursivos.
public void methodA()
{
// other code
synchronized(this)
{
methodB();
}
}
public void methodB()
{
// other code
syncrhonized(this)
{
// it can still enter this code
}
}
Imagina algo como esto:
function A():
lock (X)
B()
unlock (X)
function B():
A()
Ahora llamamos A. Sucede lo siguiente:
- Ingresamos A, bloqueando X
- Ingresamos B
- Ingresamos A de nuevo, bloqueando X nuevamente
Como nunca salimos de la primera invocación de A, X sigue bloqueado. Esto se denomina reentrada: mientras que la función A aún no ha regresado, se llama de nuevo a la función A. Si A depende de un estado global, estático, esto puede causar un "error de reingreso", donde antes de que el estado estático se limpie desde la salida de la función, la función se ejecuta nuevamente, y los valores a mitad compilados colisionan con el inicio de la segunda llamada.
En este caso, nos topamos con un candado que ya tenemos. Si la cerradura está al tanto de la reentrada, se dará cuenta de que ya somos el mismo hilo que sostiene la cerradura y nos dejará pasar. De lo contrario, se estancará para siempre, estará esperando un bloqueo que ya tiene.
En java, el lock
y el synchronized
son conscientes de la reentrada: si un hilo retiene un bloqueo y el hilo intenta volver a adquirir el mismo bloqueo, está permitido. Entonces, si escribimos el pseudocódigo anterior en Java, no se estancaría.
Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.
Java en estados de libro de prácticas: la Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.
Déjame explicarte lo que significa exactamente. En primer lugar, los bloqueos intrínsecos son reentrantes por naturaleza. La forma en que se logra la reentrada es manteniendo un contador para el número de bloqueos adquiridos y el propietario del bloqueo. Si el recuento es 0 y no hay ningún propietario asociado, significa que ningún hilo retiene el bloqueo. Cuando un hilo adquiere el bloqueo, JVM registra el propietario y establece el contador en 1. Si el mismo hilo intenta adquirir el bloqueo de nuevo, el contador se incrementa, y cuando existe el hilo propietario, se decrementa el contador de bloque sincronizado. Cuando el recuento llega a 0, nuevamente se libera el bloqueo.
Un simple ejemplo sería -
public class Test {
public synchronized void performTest() {
//...
}
}
public class CustomTest extends Test {
public synchronized void performTest() {
//...
super.performTest();
}
}
sin reentrada habría un punto muerto.