Решает ли volatile проблему гонки за данные и race condition в Java
Что проверяют
Различаете ли вы гонку данных и состояние гонки и понимаете ли, что volatile закрывает видимость и порядок операций, но не атомарность составных действий. Это надёжный вопрос на отсев в многопоточности.
Как отвечать
Ответ короткий: нет, не решает. Дальше объясните почему.
volatile даёт три вещи. Чтение и запись такой переменной не переупорядочиваются с окружающим кодом, запись устанавливает отношение happens-before с последующим чтением, а для long и double чтение и запись становятся атомарными. Этого хватает для флага остановки потока и для публикации ссылки на неизменяемый объект.
Чего он не даёт: атомарности составных операций. count++ это три действия, чтение, увеличение и запись, между которыми вклинивается другой поток, сколько бы volatile вы ни поставили. Проверка условия и действие по ней тоже составная операция.
private volatile int count;
void inc() { count++; } // инкременты теряются
private final AtomicInteger safe = new AtomicInteger();
void incSafe() { safe.incrementAndGet(); }Скажите, чем закрывать реальные случаи: атомики и compareAndSet для счётчиков, synchronized или ReentrantLock для инварианта из нескольких полей, неизменяемые объекты там, где общего состояния можно не держать. И назовите каноническое применение: поле в двойной проверке блокировки, без volatile ссылка публикуется раньше, чем объект достроен.
Где спотыкаются
Повторяют миф, что volatile запрещает процессору кэшировать переменную и чтение идёт прямо из памяти. Кэши когерентны и никто их не отключает: компилятор расставляет барьеры памяти и не держит значение в регистре, этим всё и исчерпывается.
Второе: путают термины. Гонка данных это несинхронизированный конкурентный доступ, где хотя бы один поток пишет, и для своей переменной volatile её как раз устраняет. Состояние гонки это ошибка логики, зависящая от тайминга, и она остаётся. Формулировку «не решает гонку данных» грамотный интервьюер поправит.
Третье: помечают volatile ссылку на изменяемый объект и считают потокобезопасным его содержимое. Гарантия действует на саму ссылку, а не на поля объекта или коллекции, на которые она указывает.