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 decontor.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.