← Назад к вопросам

Есть ли внутри AtomicReference синхронизация

2.8 Senior🔥 81 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Есть ли внутри AtomicReference синхронизация

Да, AtomicReference содержит встроенный механизм синхронизации, но это НЕ традиционная синхронизация через блокировки (locks). Вместо этого она использует операции Compare-and-Swap (CAS) на уровне процессора — это атомарные операции без блокировок.

Как работает AtomicReference

AtomicReference использует volatile переменные и CAS операции для обеспечения атомарности. Это позволяет ему работать намного быстрее, чем синхронизированные методы.

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceExample {
    public static void main(String[] args) throws InterruptedException {
        // Создание AtomicReference
        AtomicReference<String> ref = new AtomicReference<>("initial");
        
        // Безопасное чтение (видно все изменения других потоков)
        String value = ref.get(); // atomic read
        
        // Безопасная запись
        ref.set("new value"); // atomic write
        
        // Атомарная операция compare-and-swap (CAS)
        boolean updated = ref.compareAndSet("new value", "another");
        System.out.println("CAS successful: " + updated);
    }
}

Механизм синхронизации: volatile + CAS

1. Volatile переменная

Внутри AtomicReference используется volatile поле, что гарантирует:

  • Видимость - все изменения видны другим потокам немедленно
  • Упорядочение - операции выполняются в определённом порядке
public class AtomicReference<V> {
    private volatile V value; // Volatile - критично для синхронизации
    
    // Атомарное чтение
    public final V get() {
        return value; // volatile read
    }
    
    // Атомарная запись
    public final void set(V newValue) {
        value = newValue; // volatile write
    }
}

2. Compare-and-Swap (CAS) операция

Это аппаратная операция, которая работает атомарно на уровне процессора.

AtomicReference<User> userRef = new AtomicReference<>();

// CAS: если значение == ожидаемое, то установить новое
// Все это происходит атомарно!
User oldUser = userRef.get();
User newUser = new User("John");

boolean success = userRef.compareAndSet(oldUser, newUser);
// Если другой поток изменил значение в момент проверки,
// операция не произойдёт и вернёт false

Сравнение: AtomicReference vs Synchronized

import java.util.concurrent.atomic.AtomicReference;

public class SyncComparison {
    
    // Вариант 1: Синхронизированный
    static class SyncCounter {
        private String value = "initial";
        
        public synchronized void setValue(String newValue) {
            this.value = newValue; // Использует монитор (lock)
        }
        
        public synchronized String getValue() {
            return value; // Использует монитор (lock)
        }
    }
    
    // Вариант 2: AtomicReference (без блокировок)
    static class AtomicCounter {
        private AtomicReference<String> value = 
            new AtomicReference<>("initial");
        
        public void setValue(String newValue) {
            value.set(newValue); // Без блокировок, но атомарно!
        }
        
        public String getValue() {
            return value.get(); // Без блокировок, но атомарно!
        }
    }
}

Ключевые отличия

ХарактеристикаsynchronizedAtomicReference
МеханизмМонитор (lock)CAS (lock-free)
БлокировкаДа, другие потоки ждутНет, потоки не ждут
ПроизводительностьХороша при высокой конкуренцииОтличная при низкой-средней конкуренции
СправедливостьГарантированаНет гарантии очереди
ПростотаПроще писатьНужно понимать CAS

Практический пример: безопасное обновление ссылки

class UserCache {
    private AtomicReference<User> cachedUser = new AtomicReference<>();
    
    // Безопасное получение кеша
    public User getCachedUser() {
        return cachedUser.get();
    }
    
    // Безопасное обновление кеша
    public void updateCache(User newUser) {
        cachedUser.set(newUser);
    }
    
    // Обновление только если кеш не менялся
    public boolean updateIfEmpty(User newUser) {
        return cachedUser.compareAndSet(null, newUser);
    }
    
    // Получение и одновременно установка нового значения
    public User getAndUpdate(User newUser) {
        return cachedUser.getAndSet(newUser);
    }
}

Методы AtomicReference

AtomicReference<String> ref = new AtomicReference<>("initial");

// 1. get() - атомарное чтение
String value = ref.get();

// 2. set() - атомарная запись
ref.set("new value");

// 3. compareAndSet() - условное обновление
boolean updated = ref.compareAndSet("expected", "new");

// 4. getAndSet() - получить старое, установить новое
String oldValue = ref.getAndSet("newer");

// 5. compareAndExchange() - как CAS, но возвращает текущее значение
String actual = ref.compareAndExchange("newer", "final");

// 6. accumulateAndGet() / getAndAccumulate() - функция обновления
String result = ref.accumulateAndGet("suffix", 
    (current, operand) -> current + operand);

Когда использовать AtomicReference

Используй AtomicReference когда:

// 1. Низкая-средняя конкуренция за ресурс
public class ConfigManager {
    private AtomicReference<Config> config = new AtomicReference<>();
    
    public Config getConfig() {
        return config.get(); // Много читателей, редкие обновления
    }
    
    public void updateConfig(Config newConfig) {
        config.set(newConfig);
    }
}

// 2. Нужна простая ссылка на объект
public class SingletonFactory {
    private AtomicReference<Connection> connection = 
        new AtomicReference<>();
    
    public Connection getConnection() {
        Connection conn = connection.get();
        if (conn == null) {
            // Double-check с CAS
            connection.compareAndSet(null, createConnection());
            return connection.get();
        }
        return conn;
    }
    
    private Connection createConnection() {
        // создание подключения
        return null;
    }
}

Используй synchronized/locks когда:

// 1. Высокая конкуренция (много потоков одновременно меняют)
public class HighContentionCounter {
    private int count = 0; // ДОЛЖНО быть synchronized
    
    public synchronized void increment() {
        count++;
    }
}

// 2. Нужна синхронизация нескольких операций
public class TransferMoney {
    private int fromAccount;
    private int toAccount;
    
    // ДОЛЖНА быть синхронизирована - две операции должны быть атомарны
    public synchronized void transfer(int amount) {
        fromAccount -= amount;
        toAccount += amount;
    }
}

Внутренняя реализация (упрощённо)

public class AtomicReference<V> {
    private volatile V value;
    
    public final V get() {
        return value; // volatile read - видимость гарантирована
    }
    
    public final void set(V newValue) {
        value = newValue; // volatile write - видимость гарантирована
    }
    
    // CAS операция - выполняется атомарно на уровне процессора
    public final boolean compareAndSet(V expect, V update) {
        // Эта операция использует специальную инструкцию процессора
        // CMPXCHG (Compare and Exchange)
        // Она не может быть прервана между проверкой и установкой
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }
}

Заключение

AtomicReference содержит синхронизацию, но это lock-free синхронизация:

Внутри: volatile переменные и CAS операции (атомарные на уровне процессора) ✓ Без блокировок: потоки не ждут друг друга ✓ Быстрая: лучше чем synchronized при низкой-средней конкуренции ✓ Потокобезопасная: гарантирует безопасность при многопоточном доступе

Это главное различие между AtomicReference и synchronized - оба синхронизированы, но используют разные механизмы синхронизации на уровне реализации.