AtomicInteger.incrementAndGet în Java

Crește atomic valoarea cu 1: incrementAndGet() returnează valoarea nouă (++x), getAndIncrement() pe cea veche (x++).

Sintaxă
int incrementAndGet()
int getAndIncrement()
int decrementAndGet()
int getAndDecrement()

Ce face AtomicInteger.incrementAndGet

incrementAndGet() este varianta sigură pentru fire a lui ++x: crește valoarea și returnează valoarea nouă. getAndIncrement() corespunde lui x++: returnează valoarea de dinainte, apoi crește.

Diferența contează când folosești rezultatul. Pentru numere de ordine care încep de la 1 folosești incrementAndGet() pe un contor pornit de la 0; pentru indici care încep de la 0, getAndIncrement(). Perechile decrementAndGet() și getAndDecrement() scad cu 1.

Ce returnează

Un int: valoarea nouă (incrementAndGet) sau cea veche (getAndIncrement).

Exemple

Exemplul 1: Diferența dintre cele două

import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        AtomicInteger a = new AtomicInteger(5);
        AtomicInteger b = new AtomicInteger(5);
        System.out.println("incrementAndGet: " + a.incrementAndGet() + ", valoarea acum: " + a.get());
        System.out.println("getAndIncrement: " + b.getAndIncrement() + ", valoarea acum: " + b.get());
        System.out.println("decrementAndGet: " + a.decrementAndGet());
    }
}

Rezultat

incrementAndGet: 6, valoarea acum: 6
getAndIncrement: 5, valoarea acum: 6
decrementAndGet: 5

Exemplul 2: Numere de bon unice din mai multe fire

1000 de sarcini primesc fiecare un număr de bon. Le sortăm la final: sunt exact numerele de la 1 la 1000, fără dubluri.

import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
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) {
        AtomicInteger urmatorulBon = new AtomicInteger();
        Set<Integer> bonuri = ConcurrentHashMap.newKeySet();
        try (ExecutorService executor = Executors.newFixedThreadPool(8)) {
            for (int i = 0; i < 1000; i++) {
                executor.submit(() -> bonuri.add(urmatorulBon.incrementAndGet()));
            }
        }
        TreeSet<Integer> sortate = new TreeSet<>(bonuri);
        System.out.println("Bonuri unice: " + sortate.size());
        System.out.println("Primul: " + sortate.first() + ", ultimul: " + sortate.last());
    }
}

Rezultat

Bonuri unice: 1000
Primul: 1, ultimul: 1000

Exemplul 3: getAndIncrement ca indice într-un tablou

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        String[] coada = new String[3];
        AtomicInteger poz = new AtomicInteger();
        coada[poz.getAndIncrement()] = "Ana";
        coada[poz.getAndIncrement()] = "Mihai";
        coada[poz.getAndIncrement()] = "Ioana";
        System.out.println(Arrays.toString(coada) + ", următoarea poziție: " + poz.get());
    }
}

Rezultat

[Ana, Mihai, Ioana], următoarea poziție: 3

Greșeli frecvente

  • Folosești getAndIncrement() pentru numere care trebuie să înceapă de la 1, pe un contor de la 0. Primul număr va fi 0.
  • Crezi că contor.incrementAndGet() urmat de contor.get() dă aceeași valoare. Între ele, alt fir poate crește din nou contorul; folosește valoarea returnată.

Comenzi înrudite

  • AtomicInteger: Un int pe care mai multe fire îl pot modifica sigur, fără synchronized: get(), set(), addAndGet() și altele.
  • ConcurrentHashMap.merge: Actualizează atomic valoarea unei chei: merge adaugă sau combină (contoare), compute calculează valoarea nouă din cea veche.
  • CountDownLatch: Un numărător pentru așteptare: await() blochează până când alte fire apelează countDown() de N ori.