Вопрос проверяет понимание потокобезопасного класса AtomicInteger в Java, который используется для атомарных операций над целочисленными значениями в многопоточной среде.
AtomicInteger — это ключевой класс в Java для работы с многопоточностью, когда необходимо безопасно изменять целочисленное значение из нескольких потоков. В отличие от примитивного int или класса Integer, операции с AtomicInteger гарантируют атомарность, то есть они либо полностью выполняются, либо не выполняются вовсе, без промежуточных состояний, видимых другим потокам. Это устраняет необходимость в явной синхронизации с помощью ключевого слова synchronized для простых операций, что может повысить производительность в высококонкурентных сценариях.
В основе AtomicInteger лежит концепция сравнения с обменом (Compare-And-Swap, CAS). Метод compareAndSet сравнивает текущее значение с ожидаемым и, если они совпадают, устанавливает новое значение. Эта операция выполняется на уровне процессора как одна атомарная инструкция. Большинство других методов, таких как incrementAndGet(), используют CAS в цикле (часто называемом "spin loop" или "optimistic locking"), пока операция не завершится успешно.
AtomicInteger часто применяется для:
Пример кода, демонстрирующий использование AtomicInteger как счётчика в многопоточной среде:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AtomicExample {
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
// Запускаем 100 задач, каждая увеличивает счётчик
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
// Атомарное увеличение на 1
counter.incrementAndGet();
});
}
executor.shutdown();
// Ждём завершения всех задач
while (!executor.isTerminated()) { }
System.out.println("Final counter value: " + counter.get()); // Гарантированно 100
}
}В этом примере, даже если 10 потоков одновременно пытаются изменить counter, метод incrementAndGet() обеспечивает, что каждое увеличение будет учтено, и итоговое значение будет равно 100. Без AtomicInteger при использовании обычного int результат мог бы быть меньше из-за состояния гонки.
Использование synchronized для защиты операции инкремента также обеспечивает корректность, но может привести к блокировкам и снижению производительности при высокой конкуренции. AtomicInteger, используя неблокирующие алгоритмы (CAS), часто работает быстрее в таких условиях, так как потоки не приостанавливаются, а активно "спинятся" в цикле, пока не добьются успеха.
Вывод: AtomicInteger стоит применять, когда вам нужен простой потокобезопасный счётчик или атомарные операции над целыми числами в многопоточном приложении, и вы хотите избежать накладных расходов на явную синхронизацию. Он особенно полезен в высоконагруженных системах, где важна производительность и масштабируемость.