AtomicInteger em Java

Hoje vamos analisar AtomicInteger em Java. As operações atômicas são realizadas em uma única unidade de tarefa sem interferência de outras operações. Operações atômicas são necessárias em ambientes multithread para evitar inconsistências nos dados.

AtomicInteger

Vamos criar um programa simples multithread em que cada thread incrementa a variável compartilhada count 4 vezes. Então, se houver duas threads, após a conclusão, o valor de count deve 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) {
        // processando alguma tarefa
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Se você executar o programa acima, notará que o valor de count varia entre 5, 6, 7, 8. A razão é porque count++ não é uma operação atômica. Portanto, no momento em que uma thread lê o valor e o incrementa em um, outra thread já leu o valor anterior, levando a um resultado incorreto. Para resolver esse problema, precisamos garantir que a operação de incremento em count seja atômica. Podemos fazer isso usando Sincronização, mas a partir do Java 5, java.util.concurrent.atomic fornece classes de invólucro para inteiros e longos que podem ser usadas para realizar essa operação atômica sem o uso de Sincronização.

Exemplo de AtomicInteger Java

Aqui está o programa atualizado que sempre irá produzir o valor do contador como 8 porque o método incrementAndGet() de AtomicInteger incrementa atomicamente o valor atual em um.

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) {
        // processando algum trabalho
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Os benefícios de usar classes de Concorrência para operações atômicas é que não precisamos nos preocupar com a sincronização. Isso melhora a legibilidade do código e reduz a chance de erros. Além disso, as operações atômicas das classes de concorrência são assumidas como mais eficientes do que a sincronização, que envolve travar recursos.

Source:
https://www.digitalocean.com/community/tutorials/atomicinteger-java