¿Id = 1-id es atómico?


De la página 291 de OCP Java SE 6 Programmer Practice Exams, 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 P Q P Q

He marcado 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 salvar que 0 para estático id, pero...
  4. Boom, scheduler elige el segundo hilo a ejecutar.
  5. Entonces, el segundo hilo entra en run(). Static id sigue siendo 1, por lo que ejecuta el método pick(). P Q está impreso.
  6. Scheduler elige el primer hilo a ejecutar. Toma 0 de su pila y guarda a estático id. Así, el primer hilo también ejecuta pick() e imprime P Q.

Sin embargo, en el libro está escrito que esta respuesta es incorrecta:{[21]]}

Es incorrecta 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?

Author: Adam Stelmaszczyk, 2014-11-23

2 answers

¿Me equivoco?

No, tienes toda la razón, al igual que tu línea de tiempo de ejemplo.

Además de no ser atómico, no se garantiza que la escritura a id será 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 material de referencia como este sea incorrecto : (

 77
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-23 13:01:29

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 al mismo id 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 cada hilo:

  1. hace una copia local del valor almacenado en la dirección de memoria de id al registro de 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 el valor id se puede cambiar simultáneamente por cualquiera de los dos hilos, solo los valores inicial y final 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, id 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 de valor de id es discreto, ya sea 0 o 1.

Fin de la prueba.

Puede haber dos posibilidades para esto código:

  • Posibilidad 1. El hilo uno accede primero a la variable id. Entonces el valor de id (id = 1 - id cambia a 0. A partir de entonces, solo se ejecutará el método pick (), imprimiendo P Q. El hilo dos, evaluará id en ese momento id = 0; el método release() se ejecutará imprimiendo R S. Como resultado, se imprimirá P Q R S.

  • Posibilidad 2. El hilo dos accede primero a la variable id. Entonces el valor de id (id = 1 - id cambia a 0. A partir de entonces, sólo el método pick () ser ejecutado, imprimiendo P Q. El hilo uno, evaluará id en ese momento id = 0; el método release() se ejecutará imprimiendo R S. Como resultado, se imprimirá P Q R S.

No hay otras posibilidades. Sin embargo, cabe señalar que las variantes de P Q R S como P R Q S o R P Q S, etc. puede imprimirse debido a que pick() es un método estático y, por lo tanto, se comparte entre los dos hilos. Esto conduce a la ejecución simultánea de este método que podría resultar en la impresión de la letras en un orden diferente dependiendo de su plataforma.

Sin embargo, en cualquier caso, nunca se ejecutará el método pick() o release () dos veces como son mutuamente excluyentes. Por lo tanto, P Q P Q no será una salida.

 -1
Author: user2399800,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-27 21:43:47