ПрограммированиеBackend разработчик

Что такое 'volatile' переменная в Java и как она отличается от 'synchronized'? Когда и для чего использовать volatile?

Проходите собеседования с ИИ помощником Hintsage

Ответ

volatile — это модификатор переменной, который гарантирует видимость изменений этой переменной всеми потоками. Если переменная объявлена как volatile, то ее чтение и запись идут непосредственно из основной памяти, минуя локальные кэш-памяти потоков. Это предотвращает кеширование значений локально в потоке.

synchronized — это ключевое слово, которое обеспечивает не только видимость, но и взаимное исключение (mutual exclusion): только один поток может выполнять синхронизированный блок в один момент времени для одного объекта.

Использовать volatile переменные следует для простых флагов и счетчиков, если:

  • значение переменной не зависит от предыдущего состояния (например, счетчик только один раз пишется, множество раз читается);
  • нет необходимости в более сложной последовательности операций (check-then-act).

Пример использования volatile:

class Flag { private volatile boolean running = true; public void stop() { running = false; } public void loop() { while (running) { // ... } } }

Вопрос с подвохом

Можно ли использовать volatile для обеспечения атомарности инкремента переменной?

Ответ: Нет, volatile не обеспечивает атомарность операций. Например, counter++ не является атомарным даже если counter объявлен как volatile, потому что операция включает чтение, изменение и запись (несколько действий), которые могут быть прерваны другим потоком. Атомарность обеспечивается с помощью synchronized или классов из пакета java.util.concurrent.atomic.

Пример:

class Counter { private volatile int count = 0; public void increment() { count++; // НЕ атомарная операция! } }

Примеры реальных ошибок из-за незнания тонкостей темы


История

В проекте онлайн-магазина флаг завершения потока обработки заказов был реализован без volatile. В результате один из потоков "залипал" в бесконечном цикле, т.к. не видел изменения переменной, сделанного из другого потока. Диагностика заняла несколько дней.


История

В финансовой системе разработчик использовал volatile int для счетчика операций. В пиковых нагрузках количество операций начинало "теряться". Причина — потеря атомарности при сложных операциях инкремента.


История

Разработчики перепутали volatile и synchronized, пытаясь использовать volatile для обеспечения взаимного исключения доступа к критическим секциям, что привело к data race и трудноуловимым багам в многопоточном приложении.