Что гарантируют и что не гарантируют Atomic классы в Java
Что проверяют
Знаете ли вы границу атомарности: гарантия распространяется на одну операцию с одной переменной, и потому такие классы не заменяют синхронизацию. Вопрос отделяет тех, кто применял их осознанно, от тех, кто ставит их «для потокобезопасности».
Как отвечать
Гарантируют две вещи. Первая: атомарность одной операции чтение-изменение-запись, incrementAndGet, compareAndSet, getAndUpdate, без блокировки, поверх аппаратной инструкции сравнения с обменом. Вторая: семантику volatile, то есть видимость записи другим потокам и запрет переупорядочивания вокруг неё.
Не гарантируют согласованность двух операций подряд и тем более нескольких переменных.
if (counter.get() < LIMIT) { // между проверкой и действием
counter.incrementAndGet(); // значение успело измениться
}
counter.updateAndGet(v -> v < LIMIT ? v + 1 : v); // атомарноФункция обновления при этом обязана быть чистой: при неудачной попытке её вызовут повторно. Ещё стоит сказать, что AtomicReference делает атомарной только замену ссылки, а объект по ней потокобезопасным не становится, поэтому хранить лучше неизменяемый снимок состояния.
Где спотыкаются
Считают, что атомарная переменная делает безопасным весь код вокруг неё. Проверка и следующее за ней действие остаются гонкой, нужен цикл с compareAndSet, метод с функцией обновления или обычная блокировка.
Кладут AtomicInteger в коллекцию как ключ или ищут его через contains. Эти классы не переопределяют equals и hashCode, сравнение идёт по ссылке, поэтому поиск по равному значению ничего не находит.
Считают, что неблокирующий счётчик всегда быстрее synchronized. На слабо оспариваемом участке разница незаметна, а при большом числе потоков, бьющих в одну ячейку, попытки постоянно отбрасываются и цикл жжёт процессор. Для счётчика, который часто пишут и редко читают, есть LongAdder.