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

Кто меняет переменную volatile

1.8 Middle🔥 191 комментариев
#Основы Java

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

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

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

Кто меняет переменную volatile?

Volatile — это модификатор для переменных, которые могут изменяться из разных потоков одновременно. Ключевой момент: volatile не ограничивает, КТО может менять — это касается ЛЮБОГО потока.

Что такое volatile?

public class SharedResource {
    // ❌ Без volatile — опасно в многопоточной программе
    private boolean isRunning = true;
    
    // ✅ С volatile — безопасно
    private volatile boolean isRunning = true;
}

Volatile гарантирует:

  1. Видимость (Visibility) — изменение видно всем потокам СРАЗУ
  2. Порядок (Ordering) — операции выполняются в том порядке, в котором написаны
  3. НО НЕ атомарность — несколько операций могут пересечься

Проблема без volatile

public class Counter {
    private int value = 0;  // Без volatile
    
    public void increment() {
        value++;  // Поток 1
    }
    
    public int getValue() {
        return value;  // Поток 2
    }
}

// Проблема:
// Поток 1 может изменить value, но Поток 2 всё ещё видит старое значение
// Это называется "memory visibility" проблема

Как это работает на CPU уровне

private int x = 0;

// Поток 1            | Поток 2
// x = 5              | int copy = x;
// (копирует в локальный кэш)
//                     | // copy может быть 0, а не 5!
//                     | // Поток 2 читает из своего кэша CPU

Без volatile:

  • Каждый поток имеет свой локальный кэш переменной
  • Изменение в одном потоке может не видно другому

С volatile:

  • Переменная НЕ кэшируется
  • Каждое чтение — из основной памяти
  • Каждое изменение — в основную память

Пример: Проблема и решение

public class VisibilityProblem {
    // ❌ БЕЗ VOLATILE — опасно!
    private boolean shouldStop = false;
    
    public void setStop() {
        shouldStop = true;  // Поток 1 устанавливает
    }
    
    public void process() {
        while (!shouldStop) {  // Поток 2 проверяет
            // Бесконечный цикл!
            // Поток 2 видит кэшированное значение false
            // И никогда не видит true из Потока 1
            doWork();
        }
    }
}

// ✅ С VOLATILE — работает
public class VisibilitySolution {
    private volatile boolean shouldStop = false;
    
    public void setStop() {
        shouldStop = true;  // Гарантированно видно Потоку 2
    }
    
    public void process() {
        while (!shouldStop) {
            doWork();  // Выйдет из цикла, как только setStop() установит true
        }
    }
}

КТО может менять volatile переменную?

Любой поток! Volatile не имеет механизма запрещения доступа.

public class SharedFlag {
    private volatile boolean flag = false;
    
    public void thread1Method() {
        flag = true;   // Поток 1 может менять ✅
    }
    
    public void thread2Method() {
        flag = false;  // Поток 2 может менять ✅
    }
    
    public void thread3Method() {
        boolean value = flag;  // Поток 3 может читать ✅
    }
}

// Все потоки видят одно и то же значение из основной памяти

Когда volatile НЕ достаточно

Проблема: Compound операции

public class Counter {
    private volatile int count = 0;
    
    public void increment() {
        count++;  // ❌ НЕ АТОМАРНО!
    }
}

// Почему это проблема?
// count++ на самом деле это:
// 1. int temp = count;     // Читаем
// 2. temp = temp + 1;      // Увеличиваем
// 3. count = temp;         // Записываем

// Если два потока одновременно:
// Поток 1: читает count=5   Поток 2: читает count=5
// Поток 1: делает 5+1=6     Поток 2: делает 5+1=6
// Поток 1: пишет 6          Поток 2: пишет 6
// ИТОГ: count=6, но должен быть 7!

Решение 1: Synchronized

public class SynchronizedCounter {
    private volatile int count = 0;
    
    public synchronized void increment() {
        count++;  // Теперь атомарно
    }
}

Решение 2: AtomicInteger

public class AtomicCounter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();  // Атомарно и быстро
    }
}

Volatile для флагов (правильное использование)

public class Worker extends Thread {
    private volatile boolean running = true;
    
    public void shutdown() {
        running = false;  // Любой поток может это установить
    }
    
    @Override
    public void run() {
        while (running) {  // Будет видно изменение сразу
            doWork();
        }
    }
    
    private void doWork() {
        // Обработка
    }
}

// Использование
Worker worker = new Worker();
worker.start();

// Из основного потока:
worker.shutdown();  // Безопасно остановить worker

Ограничения volatile

private volatile int x = 0;

// ✅ БЕЗОПАСНО — одна операция
x = 5;              // volatile write
int temp = x;       // volatile read

// ❌ НЕ БЕЗОПАСНО — несколько операций
x++;                // volatile read + volatile write (race condition!)
if (x > 5) {        // volatile read
    x = 10;         // volatile write
}                   // Два потока могут вмешаться между этими операциями

Реальный пример: Двойная проверка блокировки (Double-Checked Locking)

public class Singleton {
    // ❌ БЕЗ VOLATILE — может вернуть неинициализированный объект
    private static Singleton instance;
    
    public static Singleton getInstance() {
        if (instance == null) {              // Первая проверка
            synchronized (Singleton.class) {
                if (instance == null) {      // Вторая проверка
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

// ✅ С VOLATILE — безопасно
public class Singleton {
    private static volatile Singleton instance;  // ВАЖНО: volatile!
    
    public static Singleton getInstance() {
        if (instance == null) {              // Первая проверка (без lock)
            synchronized (Singleton.class) {
                if (instance == null) {      // Вторая проверка (с lock)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Итого: Кто меняет volatile?

Ответ: ЛЮБОЙ ПОТОК может менять volatile переменную.

  • Volatile НЕ ограничивает доступ
  • Volatile НЕ запрещает никому менять переменную
  • Volatile ГАРАНТИРУЕТ видимость изменений всем потокам
  • Volatile НЕ гарантирует атомарность compound операций

Используй volatile для: ✓ Флагов (boolean) ✓ Ссылок на объекты ✓ Простых значений

Не используй volatile для: ✗ Compound операций (count++) ✗ Когда нужна взаимная исключительность ✗ Complex синхронизации

Вместо этого используй:

  • synchronized для блокирования
  • AtomicInteger, AtomicReference для атомарных операций
  • ReentrantLock для больших критических секций
Кто меняет переменную volatile | PrepBro