Clasa AtomicInteger
Clasa AtomicInteger poate fi gândită ca o implementare atomică a unui Integer, pentru că reţine o valoare de tip întreg și oferă operații atomice pe ea. Poate fi iniţializat cu o valoare sau nu, valoarea default fiind 0.
Metodele cele mai folosite sunt get() (întoarce valoarea de tip întreg), set(x) (îi setează valoarea la x), compareAndSet(x, y) (compară întregul cu x, iar, dacă cele două valori sunt egale, setează valoarea la y), addAndGet(delta) / getAndAdd(delta) (incrementează valoarea curentă cu delta și returnează valoarea nouă, respectiv pe cea veche). Un exemplu de utilizare a acestor metode poate fi observat mai jos.
AtomicInteger nr = new AtomicInteger(4);
System.out.println(nr.get()); // 4
if (nr.compareAndSet(4, 7))
System.out.println(nr.get()); // nr devine 7
System.out.println(nr.addAndGet(2)); // se aduna 2, deci devine 9
nr.set(1);
System.out.println(nr.get()); // 1
Folosirea AtomicInteger este mai avantajoasă ca performanță (mai precis din punctul de vedere al timpilor de execuție) decât folosirea de locks (blocuri synchronized), deoarece la locks avem overhead creat de acquire() (lock()) și de release() (unlock()), așa cum poate fi observat mai jos.
Exemplu
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static final int size = 1000000;
public static final int noThreads = 8;
public static int[] arr = new int[size];
public static final Object lock = new Object();
public static void main(String[] args) {
for (int i = 0; i < size; i++) {
arr[i] = i;
}
Thread[] properThreads = new Thread[noThreads];
Thread[] atomicThreads = new Thread[noThreads];
long startTime = System.nanoTime();
for (int i = 0; i < properThreads.length; i++) {
properThreads[i] = new ProperThread(i);
properThreads[i].start();
}
for (Thread properThread : properThreads) {
try {
properThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long stopTime = System.nanoTime();
System.out.println("With locks = " + (stopTime - startTime));
startTime = System.nanoTime();
for (int i = 0; i < atomicThreads.length; i++) {
atomicThreads[i] = new AtomicThread(i);
atomicThreads[i].start();
}
for (Thread atomicThread : atomicThreads) {
try {
atomicThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
stopTime = System.nanoTime();
System.out.println("With AtomicInteger = " + (stopTime - startTime));
System.out.println("Atomic sum = " + AtomicThread.sum.get());
System.out.println("Locking sum = " + ProperThread.sum);
}
}
class AtomicThread extends Thread {
public static AtomicInteger sum = new AtomicInteger(0);
private final int id;
public AtomicThread(int id) {
this.id = id;
}
@Override
public void run() {
int start = id * (int) Math.ceil((double) Main.size / Main.noThreads);
int end = Math.min(Main.size, (id + 1) * (int) Math.ceil((double) Main.size / Main.noThreads));
for (int i = start; i < end; i++) {
sum.getAndAdd(Main.arr[i]);
}
}
}
class ProperThread extends Thread {
public static int sum = 0;
private final int id;
public ProperThread(int id) {
this.id = id;
}
@Override
public void run() {
int start = id * (int) Math.ceil((double) Main.size / Main.noThreads);
int end = Math.min(Main.size, (id + 1) * (int) Math.ceil((double) Main.size / Main.noThreads));
for (int i = start; i < end; i++) {
synchronized (Main.lock) {
sum += Main.arr[i];
}
}
}
}
Java are astfel de implementări atomice precum toate tipurile primitive și nu numai (AtomicBoolean, AtomicLong, AtomicIntegerArray, etc.). Mai multe informații puteți găsi în documentația Java.