Получить доступ

Что гарантирует ключевое слово volatile для переменной?

MiddleМногопоточность
Ещё спрашивают так:
  • Чем volatile отличается от AtomicInteger?
  • Чем volatile отличается от AtomicInteger и когда какого достаточно?
  • Счётчик объявлен как volatile int counter, и несколько потоков выполняют counter++. Корректно ли это?
Коротко

volatile даёт видимость (запись сразу видна другим потокам) и запрет переупорядочивания вокруг обращения, но не атомарность: count++ на volatile-поле всё равно теряет обновления.

  • Достаточно, когда пишет один поток, а читают остальные: флаг остановки, публикация готового объекта
  • Значение вычисляется из самого себя — AtomicInteger (CAS) или synchronized
  • Запись volatile → чтение того же поля задают happens-before: видно и всё, что записано до неё

А ещё Под сильной конкуренцией за счётчик — LongAdder

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Проблема без volatile. Каждое ядро работает со своими кэшами и регистрами, а компилятор (JIT) и процессор переставляют операции. Поток может бесконечно не увидеть изменение флага, записанного другим потоком:

boolean running = true;              // без volatile
while (running) { work(); }          // JIT может вынести чтение из цикла

Что гарантирует volatile (модель памяти Java):

  • видимость — чтение всегда возвращает последнее записанное значение;
  • упорядочивание — операции не переставляются через обращение к volatile;
  • happens-before — всё, что поток сделал до записи в volatile, видно потоку, прочитавшему это значение. Поэтому volatile-ссылкой можно безопасно опубликовать полностью построенный объект.

Чего нет — атомарности. count++ — это чтение, сложение и запись. Два потока читают 5, оба пишут 6 — одно увеличение потеряно.

AtomicInteger добавляет к той же видимости атомарные операции: incrementAndGet крутит цикл CAS — «записать новое, только если в памяти всё ещё старое, иначе повторить».

Критерий выбора:

ситуациячего достаточно
один пишет, многие читают (флаг, ссылка на конфиг)volatile
новое значение зависит от старого (счётчик)AtomicInteger / AtomicLong
очень много потоков увеличивают счётчикLongAdder
несколько полей меняются согласованноsynchronized или Lock

Что хотят услышать

  • Видимость и запрет переупорядочивания, happens-before.
  • Почему count++ не атомарен.
  • Чем AtomicInteger отличается и когда хватает volatile.

Частые ошибки

  • Считать volatile заменой synchronized.
  • Делать volatile-счётчик с ++.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Атомарность составных операций, например i++
  • ✓Видимость изменений между потоками и запрет переупорядочивания, но не атомарность составных операций
  • ✗Полную потокобезопасность любого кода с этой переменной
  • ✗Хранение значения в регистре потока для скорости

Что спросят дальше

  • Что такое happens-before?
  • Когда брать AtomicInteger вместо volatile?
  • Что такое проблема ABA и чем помогает AtomicStampedReference?
  • Почему double-checked locking требует volatile?
  • Чем LongAdder лучше AtomicInteger под высокой конкуренцией?
  • Что такое ABA-проблема в CAS?

Разбор темы

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем