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