0Pricing
Java Academy · Урок

Атомарные переменные: обновления без блокировок

Используйте AtomicInteger, AtomicLong и AtomicReference для потокобезопасных счётчиков без блокировок.

«Атомарные переменные: обновления без блокировок» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Проблема неатомарных операций

Простое увеличение count++ НЕ является потокобезопасным: это три операции — чтение, сложение и запись. Параллельные потоки могут перемежаться при их выполнении, что приводит к потере обновлений. Атомарные переменные решают эту проблему без блокировок.

// UNSAFE:
int count = 0;
// Thread A reads count=0, Thread B reads count=0
// Both write 1 — second update lost!
count++; // NOT thread-safe

// SAFE:
import java.util.concurrent.atomic.*;
AtomicInteger atomicCount = new AtomicInteger(0);
atomicCount.incrementAndGet(); // atomic, lock-free

Основы AtomicInteger

AtomicInteger предоставляет целочисленные операции, атомарность которых гарантируется инструкциями CPU сравнения и обмена (CAS), поэтому блокировки не нужны.

AtomicInteger counter = new AtomicInteger(0);

counter.set(10);                   // set to 10
System.out.println(counter.get()); // 10

int prev = counter.getAndIncrement(); // returns 10, sets to 11
int curr = counter.incrementAndGet(); // sets to 12, returns 12
counter.addAndGet(5);                 // adds 5, returns 17
counter.decrementAndGet();            // subtracts 1, returns 16

compareAndSet: операция CAS

compareAndSet(expected, update) атомарно обновляет значение, только если оно в данный момент равно ожидаемому значению. Возвращает положительный результат при успехе. Это основа всех алгоритмов без блокировок.

AtomicInteger ai = new AtomicInteger(5);

boolean updated = ai.compareAndSet(5, 10); // current==5? set to 10
System.out.println(updated); // true
System.out.println(ai.get()); // 10

boolean failed = ai.compareAndSet(5, 20); // current==10, not 5 → fails
System.out.println(failed); // false
System.out.println(ai.get()); // still 10

AtomicLong для счётчиков

AtomicLong — эквивалент для 64-разрядных значений. Обычно используется для генераторов последовательностей и счётчиков обращений:

AtomicLong seq = new AtomicLong(0);

// Thread-safe sequence generator:
long nextId() {
    return seq.incrementAndGet();
}

System.out.println(nextId()); // 1
System.out.println(nextId()); // 2
System.out.println(nextId()); // 3

AtomicBoolean

Потокобезопасный логический флаг. Часто используется для одноразовой инициализации или сигналов завершения работы:

AtomicBoolean initialized = new AtomicBoolean(false);

void initOnce() {
    // compareAndSet: only first caller succeeds
    if (initialized.compareAndSet(false, true)) {
        System.out.println("Initializing...");
        // perform expensive init
    } else {
        System.out.println("Already initialized");
    }
}

AtomicReference

AtomicReference<V> предоставляет атомарное обновление ссылки на объект. Используется для реализации структур данных без блокировок:

import java.util.concurrent.atomic.*;

record Config(String host, int port) {}

AtomicReference<Config> configRef =
    new AtomicReference<>(new Config("localhost", 8080));

// Hot-swap the configuration atomically:
Config oldConfig = configRef.get();
Config newConfig = new Config("prod.example.com", 443);

boolean swapped = configRef.compareAndSet(oldConfig, newConfig);
System.out.println(swapped); // true
System.out.println(configRef.get()); // Config[host=prod.example.com, port=443]

updateAndGet / getAndUpdate

Атомарно применяйте функцию, используя внутри цикл повторных попыток CAS:

AtomicInteger value = new AtomicInteger(10);

// Apply function atomically: square the value
int newVal = value.updateAndGet(x -> x * x);
System.out.println(newVal); // 100

// Get old, then apply:
int old = value.getAndUpdate(x -> x + 1);
System.out.println(old);   // 100
System.out.println(value.get()); // 101

accumulateAndGet

Объединяйте текущее значение с заданным значением с помощью функции:

AtomicInteger max = new AtomicInteger(0);

// Keep running maximum:
max.accumulateAndGet(42, Math::max); // max(0, 42) = 42
max.accumulateAndGet(15, Math::max); // max(42, 15) = 42
max.accumulateAndGet(99, Math::max); // max(42, 99) = 99

System.out.println(max.get()); // 99

LongAdder для счётчиков при высокой конкуренции

При высокой конкуренции между потоками повторные попытки CAS в AtomicLong могут снижать производительность. LongAdder использует несколько ячеек, чтобы уменьшить конкуренцию:

import java.util.concurrent.atomic.*;

LongAdder adder = new LongAdder();

// Multiple threads can call add() with minimal contention:
adder.increment();
adder.add(5);
adder.increment();

System.out.println(adder.sum()); // 7
// Note: sum() is NOT atomic with add() — use only when no concurrent adds

Атомарные массивы

AtomicIntegerArray и AtomicLongArray предоставляют атомарные операции над отдельными элементами массива:

AtomicIntegerArray arr = new AtomicIntegerArray(5);

arr.set(0, 10);
arr.incrementAndGet(0);
arr.compareAndSet(2, 0, 42); // index 2: if 0, set 42

System.out.println(arr.get(0)); // 11
System.out.println(arr.get(2)); // 42

Когда использовать атомарные переменные, а когда блокировки

Используйте атомарные переменные, если:

  • обновляется одна переменная (счётчик, флаг или ссылка);
  • конкуренция невысока;
  • важна производительность без блокировок.

Используйте блокировки, если:

  • несколько переменных необходимо обновлять атомарно;
  • требуется сложная условная логика;
  • конкуренция очень высокая (повторные попытки CAS снижают производительность).

Быстрая проверка

Что гарантирует compareAndSet(expected, update)?

Итоги: атомарные переменные

Главное:

  • Атомарные типы используют инструкции CPU CAS — они не требуют блокировок и потокобезопасны
  • AtomicInteger, AtomicLong, AtomicBoolean и AtomicReference обеспечивают атомарность одной переменной
  • compareAndSet — основа алгоритмов без блокировок
  • updateAndGet и accumulateAndGet предназначены для функциональных обновлений
  • LongAdder подходит для сценариев со счётчиками при высокой конкуренции

Часто задаваемые вопросы

Урок «Атомарные переменные: обновления без блокировок» бесплатный?

Да — полный текст урока «Атомарные переменные: обновления без блокировок» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Атомарные переменные: обновления без блокировок»?

Используйте AtomicInteger, AtomicLong и AtomicReference для потокобезопасных счётчиков без блокировок. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Атомарные переменные: обновления без блокировок»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. ReentrantLock и synchronized
  2. ReadWriteLock для сценариев чтения и записи
  3. Атомарные переменные: обновления без блокировок
  4. StampedLock и оптимистичное чтение
← Назад к Java Academy