AtomicInteger în Java

Un int pe care mai multe fire îl pot modifica sigur, fără synchronized: get(), set(), addAndGet() și altele.

Sintaxă
import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger contor = new AtomicInteger(valoareInitiala);
int get()
void set(int valoare)
int addAndGet(int delta)
int getAndAdd(int delta)
boolean compareAndSet(int asteptat, int nou)

Ce face AtomicInteger

Operația x++ sau x += 5 pe un int obișnuit are trei pași (citește, adună, scrie). Dacă două fire o fac în același timp, o actualizare se poate pierde. AtomicInteger face fiecare operație într-un singur pas indivizibil (atomic).

get() citește valoarea curentă, set(v) o înlocuiește. addAndGet(delta) adună și returnează valoarea nouă; getAndAdd(delta) adună și returnează valoarea veche. compareAndSet(asteptat, nou) schimbă valoarea doar dacă este încă cea așteptată.

Obiectul AtomicInteger poate fi folosit și din lambda (variabila este finală, conținutul se schimbă), deci este un contor comod pentru sarcini trimise unui executor. Există și AtomicLong, AtomicBoolean și AtomicReference.

Parametri

Parametru Descriere
valoareInitiala Valoarea de pornire; implicit 0.
delta Cât se adună (poate fi negativ).

Ce returnează

get() returnează valoarea curentă; addAndGet valoarea după adunare.

Exemple

Exemplul 1: int obișnuit față de AtomicInteger

Două fire adună câte 100.000. Contorul atomic ajunge mereu la 200.000. Contorul obișnuit pierde de obicei o parte din adunări, deci rezultatul lui diferă la fiecare rulare: afișăm doar dacă este corect.

import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    static int simplu = 0;

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger atomic = new AtomicInteger();
        Runnable sarcina = () -> {
            for (int i = 0; i < 100_000; i++) {
                simplu++;
                atomic.addAndGet(1);
            }
        };
        Thread a = new Thread(sarcina);
        Thread b = new Thread(sarcina);
        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println("AtomicInteger: " + atomic.get());
        System.out.println("int obișnuit este 200000? " + (simplu == 200_000));
    }
}

Rezultat posibil (diferă la fiecare rulare)

AtomicInteger: 200000
int obișnuit este 200000? false

Exemplul 2: addAndGet, getAndAdd și set

import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        AtomicInteger puncte = new AtomicInteger(100);
        System.out.println("addAndGet(50): " + puncte.addAndGet(50));
        System.out.println("getAndAdd(-30): " + puncte.getAndAdd(-30));
        System.out.println("get(): " + puncte.get());
        puncte.set(0);
        System.out.println("după set(0): " + puncte);
    }
}

Rezultat

addAndGet(50): 150
getAndAdd(-30): 150
get(): 120
după set(0): 0

Exemplul 3: Suma vânzărilor din mai multe sarcini

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        int[][] casierii = {{12, 40, 7}, {99, 15}, {23, 23, 23, 31}};
        AtomicInteger total = new AtomicInteger();
        try (ExecutorService executor = Executors.newFixedThreadPool(3)) {
            for (int[] bonuri : casierii) {
                executor.submit(() -> {
                    for (int b : bonuri) total.addAndGet(b);
                });
            }
        }
        System.out.println("Încasări totale: " + total.get() + " lei");
        System.out.println("compareAndSet(273, 0): " + total.compareAndSet(273, 0) + ", acum " + total.get());
    }
}

Rezultat

Încasări totale: 273 lei
compareAndSet(273, 0): true, acum 0

Greșeli frecvente

  • Scrii contor.set(contor.get() + 1). Sunt două operații separate și o actualizare se poate pierde; folosește incrementAndGet() sau addAndGet(1).
  • Compari două AtomicInteger cu equals. Nu compară valorile (compară obiectele); compară a.get() == b.get().

Comenzi înrudite

  • AtomicInteger.incrementAndGet: Crește atomic valoarea cu 1: incrementAndGet() returnează valoarea nouă (++x), getAndIncrement() pe cea veche (x++).
  • ConcurrentHashMap.merge: Actualizează atomic valoarea unei chei: merge adaugă sau combină (contoare), compute calculează valoarea nouă din cea veche.
  • ReentrantLock: Un lacăt: lock() lasă un singur fir să intre în zona protejată, unlock() îl eliberează; unlock se pune mereu în finally.
  • ExecutorService.close: Oprește executorul și așteaptă să se termine toate sarcinile; permite folosirea executorului în try-with-resources.