Что гарантирует ключевое слово volatile для переменной?
- Чем 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?
Разбор темы
- volatile, атомарность и CAS
Разница видимости и атомарности, atomic-классы, compare-and-swap и ABA, контеншн и LongAdder.
- Java Memory Model: happens-before и volatile
Зачем нужна модель памяти, видимость и переупорядочивание, отношение happens-before, семантика volatile и final, гонки данных.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.