Для чего используется ключевое слово volatile в Java?

«Для чего используется ключевое слово volatile в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключевое слово volatile в Java гарантирует видимость изменений переменной для всех потоков и запрещает реордеринг операций с ней, обеспечивая слабую форму синхронизации.

Что оно гарантирует?

  1. Видимость (Memory Visibility): Когда один поток записывает значение в volatile-переменную, это изменение немедленно становится видимым для всех других потоков. Без volatile изменение может остаться в кэше процессора и быть невидимым для других потоков.
  2. Запрет реордеринга: Операции чтения/записи volatile-переменной не могут быть переупорядочены компилятором или процессором относительно других операций, что создаёт точки happens-before в модели памяти Java.

Пример использования (флаг завершения):

class WorkerThread implements Runnable {
    private volatile boolean running = true; // Критично — volatile!

    public void run() {
        while (running) { // Поток увидит изменение из main-потока
            // Работа...
        }
    }
    public void stop() {
        running = false; // Сигнал на остановку
    }
}

Важные ограничения и best practices:

  • НЕ атомарность сложных операций: Операции вроде count++ (чтение-изменение-запись) не атомарны, даже если count объявлен как volatile. Для таких случаев используйте AtomicInteger.
  • Когда применять: Идеально для простых флагов состояния (boolean running) или переменных, записываемых одним потоком и читаемых многими.
  • Чего не хватает: volatile не обеспечивает взаимное исключение (mutual exclusion). Если несколько потоков могут изменять переменную, требуется synchronized или атомарные классы (java.util.concurrent.atomic).

Итог: volatile — это инструмент для управления видимостью, а не для обеспечения атомарности составных действий.