Hoy vamos a examinar AtomicInteger
en Java. Las operaciones atómicas se realizan en una unidad única de tarea sin interferencia de otras operaciones. Las operaciones atómicas son necesarias en un entorno multi-hilo para evitar inconsistencias de datos.
AtomicInteger
Creemos un programa multi-hilo simple donde cada hilo incrementa la variable compartida
count
4 veces. Así que si hay dos hilos, después de que terminen, el valor de count
debería ser 8. JavaAtomic.java
package com.journaldev.concurrency;
public class JavaAtomic {
public static void main(String[] args) throws InterruptedException {
ProcessingThread pt = new ProcessingThread();
Thread t1 = new Thread(pt, "t1");
t1.start();
Thread t2 = new Thread(pt, "t2");
t2.start();
t1.join();
t2.join();
System.out.println("Processing count=" + pt.getCount());
}
}
class ProcessingThread implements Runnable {
private int count;
@Override
public void run() {
for (int i = 1; i < 5; i++) {
processSomething(i);
count++;
}
}
public int getCount() {
return this.count;
}
private void processSomething(int i) {
// procesando algún trabajo
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Si ejecutas el programa anterior, notarás que el valor de count
varía entre 5, 6, 7, 8. La razón es porque count++ no es una operación atómica. Así que para cuando un hilo lee su valor y lo incrementa en uno, otro hilo ha leído el valor anterior, lo que lleva a un resultado incorrecto. Para resolver este problema, debemos asegurarnos de que la operación de incremento en count sea atómica, podemos hacerlo usando Sincronización pero Java 5 java.util.concurrent.atomic
proporciona clases envolventes para int y long que pueden usarse para lograr esta operación atómica sin el uso de Sincronización.
Ejemplo de Java AtomicInteger
Aquí está el programa actualizado que siempre mostrará el valor del contador como 8 porque el método incrementAndGet()
de AtomicInteger
incrementa atómicamente el valor actual en uno.
package com.journaldev.concurrency;
import java.util.concurrent.atomic.AtomicInteger;
public class JavaAtomic {
public static void main(String[] args) throws InterruptedException {
ProcessingThread pt = new ProcessingThread();
Thread t1 = new Thread(pt, "t1");
t1.start();
Thread t2 = new Thread(pt, "t2");
t2.start();
t1.join();
t2.join();
System.out.println("Processing count=" + pt.getCount());
}
}
class ProcessingThread implements Runnable {
private AtomicInteger count = new AtomicInteger();
@Override
public void run() {
for (int i = 1; i < 5; i++) {
processSomething(i);
count.incrementAndGet();
}
}
public int getCount() {
return this.count.get();
}
private void processSomething(int i) {
// procesando algún trabajo
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Los beneficios de usar clases de concurrencia para operaciones atómicas es que no necesitamos preocuparnos por la sincronización. Esto mejora la legibilidad del código y reduce la posibilidad de errores. Además, se asume que las clases de concurrencia para operaciones atómicas son más eficientes que la sincronización, que implica bloquear recursos.
Source:
https://www.digitalocean.com/community/tutorials/atomicinteger-java