Estoy tratando de aprender la concurrencia en Java, pero haga lo que haga, 2 subprocesos se ejecutan en serie, no en paralelo, por lo que no puedo replicar los problemas comunes de concurrencia explicados en los tutoriales (como interferencia de subprocesos y errores de coherencia de memoria). Código de muestra:

public class Synchronization {
static int v;

public static void main(String[] args) {

    Runnable r0 = () -> {
        for (int i = 0; i < 10; i++) {
            Synchronization.v++;
            System.out.println(v);
        }
    };

    Runnable r1 = () -> {
        for (int i = 0; i < 10; i++) {
            Synchronization.v--;
            System.out.println(v);
        }
    };

    Thread t0 = new Thread(r0);
    Thread t1 = new Thread(r1);
    t0.start();
    t1.start();

}

}

Esto siempre me da un resultado que comienza en 1 y termina en 0 ( cualquiera que sea la longitud del bucle ). Por ejemplo, el código anterior me da cada vez:

1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1 0

A veces, el segundo subproceso comienza primero y los resultados son los mismos pero negativos, por lo que todavía se ejecuta en serie.

Intenté en Intellij y Eclipse con resultados idénticos. La CPU tiene 2 núcleos si es importante.

ACTUALIZACIÓN: finalmente se volvió reproducible con enormes bucles (a partir de 1_000_000), aunque todavía no siempre y solo con una pequeña cantidad de discrepancia final. También parece que las operaciones en bucles son "más pesadas", como imprimir el nombre del hilo también lo hace más reproducible. Agregar manualmente dormir al hilo también funciona, pero hace que el experimento sea menos limpio, por así decirlo. La razón no parece ser que el primer ciclo finalice antes de que comience el segundo, porque veo que ambos ciclos se imprimen en la consola mientras continúo operando y todavía me da 0 al final. Las razones parecen más una carrera de hilos para la misma variable. Profundizaré en eso, gracias.

1
takeoff 13 sep. 2018 a las 09:47

3 respuestas

La mejor respuesta

Parece que el primer subproceso nunca da la oportunidad de que el segundo en Thread Race tome una variable / segundo, simplemente nunca tiene tiempo para comenzar (no podría decirlo con certeza), por lo que el segundo casi * siempre estará esperando hasta el primero El bucle estará terminado.

Algunas operaciones pesadas mezclarán el resultado: TimeUnit.MILLISECONDS.sleep(100);

* no siempre es cierto, pero tuvo suerte en sus pruebas

2
Ivan Baranuk 13 sep. 2018 a las 07:13

Iniciar un subproceso es una operación pesada, lo que significa que llevará un tiempo realizarlo. Debido a ese hecho, para el momento en que comienza el segundo hilo, el primero está terminado.

El razonamiento por el que a veces está en "orden de reversión" se debe a cómo funciona el planificador de subprocesos. Según las especificaciones, no hay garantías sobre el orden de ejecución del subproceso: teniendo esto en cuenta, sabemos que es posible que el segundo subproceso se ejecute primero (y finalice)

Aumente el recuento de iteraciones a algo significativo como 10000 y vea qué sucederá entonces.

1
Antoniossss 13 sep. 2018 a las 07:09

Esto se llama un momento de suerte según Brian Goetz (Autor de Java Concurrency In Practice). Como no hay sincronización con la variable estática v, está claro que esta clase no es segura para subprocesos.

1
samzz 20 sep. 2018 a las 04:50