thread lock example java multithreading atomic swap scjp

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:

  1. Estamos comenzando dos hilos.
  2. Primero entra run() .
  3. De acuerdo con JLS 15.26.1 , primero evalúa 1 - id . El resultado es 0 . Se almacena en la pila del hilo. Estamos a punto de guardar ese 0 en la id estática, pero ...
  4. Boom, el planificador elige el segundo subproceso para ejecutar.
  5. Entonces, el segundo hilo ingresa run() . La id estática sigue siendo 1 , por lo que ejecuta el método pick() . PQ está impreso.
  6. El programador elige el primer subproceso para ejecutar. Toma 0 de su pila y lo guarda en la id estática. Entonces, el primer hilo también ejecuta pick() e imprime PQ .

Sin embargo, en el libro está escrito que esta respuesta es incorrecta:

Es incorrecto porque la línea id = 1 - id intercambia el valor de id entre 0 y 1 . 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:

  1. realiza una copia local del valor almacenado en la dirección de memoria de la identificación en el registro de la CPU;
  2. realiza la operación 1 - id . Estrictamente hablando, aquí se realizan dos operaciones (-id and +1) ;
  3. 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étodo pick () , imprimiendo PQ . El segundo hilo evaluará id en ese momento id = 0 ; la release() método release() 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étodo pick () , imprimiendo PQ . El hilo uno, evaluará id en ese momento id = 0 ; la release() método release() 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.