lock - java thread pool
¿Es id=1-id atómico? (2)
¿Me equivoco?
No, tienes toda la razón, como es tu ejemplo de línea de tiempo.
Además de no ser atómico, no se garantiza que la escritura en la
id
sea recogida por el otro hilo de todos modos, dado que no hay sincronización y el campo no es volátil.
Es algo desconcertante que el material de referencia como este sea incorrecto :(
De la página 291 de los exámenes de práctica de programador OCP Java SE 6, pregunta 25:
public class Stone implements Runnable {
static int id = 1;
public void run() {
id = 1 - id;
if (id == 0)
pick();
else
release();
}
private static synchronized void pick() {
System.out.print("P ");
System.out.print("Q ");
}
private synchronized void release() {
System.out.print("R ");
System.out.print("S ");
}
public static void main(String[] args) {
Stone st = new Stone();
new Thread(st).start();
new Thread(st).start();
}
}
Una de las respuestas es:
La salida podría ser
PQPQ
Marqué esta respuesta como correcta. Mi razonamiento:
- Estamos comenzando dos hilos.
-
Primero entra
run()
. -
De acuerdo con
JLS 15.26.1
, primero evalúa
1 - id
. El resultado es0
. Se almacena en la pila del hilo. Estamos a punto de guardar ese0
en laid
estática, pero ... - Boom, el planificador elige el segundo subproceso para ejecutar.
-
Entonces, el segundo hilo ingresa
run()
. Laid
estática sigue siendo1
, por lo que ejecuta el métodopick()
.PQ
está impreso. -
El programador elige el primer subproceso para ejecutar.
Toma
0
de su pila y lo guarda en laid
estática. Entonces, el primer hilo también ejecutapick()
e imprimePQ
.
Sin embargo, en el libro está escrito que esta respuesta es incorrecta:
Es incorrecto porque la línea
id = 1 - id
intercambia el valor deid
entre0
y1
. No hay posibilidad de que el mismo método se ejecute dos veces.
No estoy de acuerdo Creo que hay alguna posibilidad para el escenario que presenté anteriormente. Tal intercambio no es atómico. ¿Me equivoco?
En mi opinión, la respuesta en los Exámenes de práctica es correcta. En este código, está ejecutando dos subprocesos que tienen acceso a la misma identificación de variable estática. Las variables estáticas se almacenan en el montón en Java, no en la pila. El orden de ejecución de los runnables es impredecible.
Sin embargo, para cambiar el valor de id de cada hilo:
- realiza una copia local del valor almacenado en la dirección de memoria de la identificación en el registro de la CPU;
-
realiza la operación
1 - id
. Estrictamente hablando, aquí se realizan dos operaciones(-id and +1)
; -
mueve el resultado de nuevo al espacio de memoria de
id
en el montón.
Esto significa que, aunque cualquiera de los dos subprocesos puede cambiar el valor de identificación simultáneamente, solo los valores iniciales y finales son mutables. Los valores intermedios no serán modificados entre sí.
Además, el análisis del código puede mostrar que en cualquier momento, la identificación solo puede ser 0 o 1.
Prueba:
-
Valor inicial id = 1; Un hilo lo cambiará a 0 (
id = 1 - id
). Y el otro hilo lo devolverá a 1. -
Valor inicial id = 0; Un hilo lo cambiará a 1 (
id = 1 - id
). Y el otro hilo lo devolverá a 0.
Por lo tanto, el estado del valor de id es discreto 0 o 1.
Fin de la prueba.
Puede haber dos posibilidades para este código:
-
Posibilidad 1. El subproceso uno accede primero a la identificación de la variable. Luego, el valor de id (
id = 1 - id
cambia a 0. A partir de entonces, solo se ejecutará el métodopick ()
, imprimiendoPQ
. El segundo hilo evaluará id en ese momentoid = 0
; larelease()
métodorelease()
será entonces ejecutando la impresión R S. Como resultado, se imprimiráPQRS
. -
Posibilidad 2. Enhebre dos accesos a la variable ID primero. Luego, el valor de id (
id = 1 - id
cambia a 0. A partir de entonces, solo se ejecutará el métodopick ()
, imprimiendoPQ
. El hilo uno, evaluará id en ese momentoid = 0
; larelease()
métodorelease()
será entonces ejecutando la impresión R S. Como resultado, se imprimiráPQRS
.
No hay otras posibilidades.
Sin embargo, debe tenerse en cuenta que las variantes de
PQRS
como
PRQS
o
RPQS
, etc. pueden imprimirse debido a que
pick()
es un método estático y, por lo tanto, se comparte entre los dos hilos.
Esto lleva a la ejecución simultánea de este método, lo que podría dar como resultado la impresión de las letras en un orden diferente según su plataforma.
Sin embargo, en cualquier caso, el método
pick()
o
release ()
nunca se ejecutará dos veces, ya que son
mutuamente excluyentes
.
Por
PQPQ
tanto,
PQPQ
no será una salida.