ENIGMA AI
ENIGMA AI

Что такое атомарность?

встречается 5× middle concurrency

Как ответить

Атомарность — это свойство операции или набора операций, которые выполняются как единое неделимое целое. Если операция атомарна, то она либо выполняется полностью, либо не выполняется вовсе. Никакого промежуточного состояния не существует. В контексте concurrency это гарантирует, что один поток не увидит частичного результата другого потока.

На уровне процессора атомарность обеспечивается специальными инструкциями. Например, в x86 это инструкция CMPXCHG (compare-and-exchange) с префиксом LOCK, который блокирует шину памяти или кэш-линию на время выполнения. В Java для примитивных типов (кроме long и double) чтение и запись атомарны по спецификации JVM. Для long и double — нет, если они не объявлены как volatile. В C++ атомарность обеспечивается через std::atomic, который внутри использует те же CMPXCHG или барьеры памяти.

Главная проблема, которую решает атомарность, — это race condition. Допустим, два потока одновременно инкрементируют счётчик. Без атомарности операция counter++ разбивается на три шага: чтение, сложение, запись. Если потоки пересекутся, одно приращение потеряется. Атомарный инкремент (atomic_fetch_add или synchronized блок) гарантирует, что вся последовательность выполняется без прерывания.

Атомарность бывает на разных уровнях:

  • Аппаратная — инструкции процессора (CAS, LL/SC).
  • Языковаяvolatile в Java/C# или std::atomic в C++.
  • Транзакционная — STM (Software Transactional Memory), где набор операций выполняется как атомарная транзакция с откатом при конфликте.
  • Базы данных — ACID-транзакции, где атомарность означает, что все изменения в рамках транзакции применяются или откатываются целиком.

На практике атомарность не бесплатна. Аппаратные блокировки (LOCK-префикс) сбрасывают конвейер процессора и замедляют соседние ядра. Поэтому в высокопроизводительном коде стараются минимизировать атомарные операции: используют локальные копии данных, lock-free структуры на CAS, или разделяют данные так, чтобы каждый поток работал со своей копией (thread-local storage).

Пример на C++:

#include <atomic>
#include <thread>

std::atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 1000; ++i) {
        counter.fetch_add(1, std::memory_order_relaxed);
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    // counter == 2000 гарантированно
}

Здесь fetch_add — атомарная операция. Даже если два потока выполняются одновременно, счётчик всегда будет корректен. Без std::atomic результат мог бы быть меньше 2000 из-за race condition.

Важно помнить: атомарность не то же самое, что потокобезопасность. Атомарная операция защищает только одну конкретную операцию. Если у вас есть несколько атомарных операций подряд, между ними может вмешаться другой поток. Для композиции атомарных действий нужны блокировки или транзакционная память.

Ключевые тезисы

  • Атомарность — неделимость операции: выполняется целиком или не выполняется.
  • Аппаратная поддержка: CMPXCHG с LOCK-префиксом в x86, LL/SC в ARM.
  • Решает проблему race condition на уровне отдельной операции.
  • Не равна потокобезопасности: композиция атомарных операций требует синхронизации.
  • Цена атомарности: блокировка шины/кэша, замедление соседних ядер.

Что спросят дальше

  • — Чем отличается атомарность от видимости (visibility)? Приведите пример, когда volatile достаточно, а atomic — нет.
  • — Что такое ABA-проблема в контексте CAS-операций и как её решают?
  • — Как бы вы реализовали lock-free стек с использованием атомарных операций?

Готовьтесь к собеседованию с ENIGMA AI

AI-суфлёр подсказывает ответы прямо на собеседовании в реальном времени — незаметно для интервьюера.

Скачать приложение