AtomicInteger em Java

Hoje vamos analisar o AtomicInteger em Java. As operações atômicas são realizadas em uma única unidade de tarefa sem interferência de outras operações. As operações atômicas são necessárias em um ambiente com várias threads para evitar inconsistência de dados.

AtomicInteger

Vamos criar um programa simples com várias threads onde cada thread incrementa a variável compartilhada count 4 vezes. Então, se houver duas threads, após elas terminarem, 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 algum trabalho
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Se você executar o programa acima, perceberá que o valor de count varia entre 5, 6, 7, 8. O motivo é porque count++ não é uma operação atômica. Portanto, no momento em que uma thread lê seu valor e o incrementa em um, outra thread já leu o valor anterior, levando a um resultado incorreto. Para resolver esse problema, teremos que garantir que a operação de incremento em count seja atômica, podemos fazer isso usando Sincronização, mas o Java 5 java.util.concurrent.atomic fornece classes de invólucro para int e long que podem ser usadas para alcançar essa operação atômica sem o uso de Sincronização.

Exemplo de AtomicInteger em Java

Aqui está o programa atualizado que sempre retornará o valor do contador como 8 porque o método incrementAndGet() do 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 sincronização. Isso melhora a legibilidade do código e reduz a chance de erros. Além disso, as operações atômicas em classes de concorrência são consideradas mais eficientes do que a sincronização, que envolve o travamento de recursos.

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