AtomicInteger en Java

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