Вопрос с собеседования · Java

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

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

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

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

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

Подробно

Проблема без 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++
  • ✓Видимость изменений между потоками и запрет переупорядочивания
  • ✗Полную потокобезопасность любого кода с этой переменной
  • ✗Видимость и атомарность любых операций, включая i++

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

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

Разбор темы

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

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования