¿Por qué i++
no es atómico en Java?
Para profundizar un poco más en Java, traté de contar con qué frecuencia se ejecuta el bucle en los subprocesos.
Entonces usé un
private static int total = 0;
en la clase principal.
Tengo dos hilos.
- Hilo 1: Impresiones
System.out.println("Hello from Thread 1!");
- Hilo 2: Impresiones
System.out.println("Hello from Thread 2!");
Y cuento las líneas impresas por el hilo 1 y el hilo 2. Pero las líneas del hilo 1 + las líneas del hilo 2 no coinciden con el número total de líneas impresas.
Aquí está mi código:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test {
private static int total = 0;
private static int countT1 = 0;
private static int countT2 = 0;
private boolean run = true;
public Test() {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
newCachedThreadPool.execute(t1);
newCachedThreadPool.execute(t2);
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
run = false;
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println((countT1 + countT2 + " == " + total));
}
private Runnable t1 = new Runnable() {
@Override
public void run() {
while (run) {
total++;
countT1++;
System.out.println("Hello #" + countT1 + " from Thread 2! Total hello: " + total);
}
}
};
private Runnable t2 = new Runnable() {
@Override
public void run() {
while (run) {
total++;
countT2++;
System.out.println("Hello #" + countT2 + " from Thread 2! Total hello: " + total);
}
}
};
public static void main(String[] args) {
new Test();
}
}
iinc
operación para incrementar enteros, pero eso solo funciona para variables locales, donde la concurrencia no es una preocupación. Para los campos, el compilador genera comandos de lectura, modificación y escritura por separado.
iinc
instrucción para los campos, que tiene una sola instrucción no garantiza la atomicidad, por ejemplo, no volatile
long
y double
el acceso de campo en no garantiza que sea atómica sin tener en cuenta el hecho de que se lleva a cabo por una sola instrucción de código de bytes.
AtomicInteger
?