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șteincrementAndGet()sauaddAndGet(1). - Compari două
AtomicIntegercuequals. 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.