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

Один поток в цикле читает обычное поле-флаг, другой его меняет, но цикл не завершается. Почему?

JuniorJVM и сборка мусора
Ещё спрашивают так:
  • Что делает volatile?
  • Что такое проблема видимости в многопоточности?
  • Чем volatile отличается от synchronized?
Коротко

Проблема видимости: без синхронизации нет гарантии, что поток увидит изменение, сделанное другим, — нужен volatile или синхронизация.

  • Компилятор и процессор вправе кэшировать значение и переупорядочивать операции
  • volatile даёт видимость и порядок (happens-before), но не атомарность count++

А ещё Для флага остановки volatile достаточно, для счётчика — нет

Подробно

Java Memory Model разрешает компилятору (в том числе JIT) и процессору оптимизации: держать значение поля в регистре или кэше ядра, переупорядочивать независимые операции. Для одного потока это незаметно, но другой поток может бесконечно читать старое значение. JIT вправе превратить while (!stop) {} в бесконечный цикл, прочитав stop один раз.

volatile на поле даёт две гарантии:

  • видимость — запись сразу видна всем потокам, чтение всегда берёт актуальное значение;
  • порядок — запись в volatile-поле «публикует» всё, что поток записал до неё: другой поток, прочитавший volatile, увидит и эти изменения (отношение happens-before).

Чего volatile не даёт — атомарности составных операций. count++ — это чтение, прибавление и запись; два потока всё равно потеряют обновление. Для этого нужны AtomicInteger или блокировка.

Типичное правильное применение volatile — флаг остановки, признак готовности, безопасная публикация неизменяемого объекта.

Пример

class Worker implements Runnable {
    private volatile boolean stopped;   // без volatile цикл может не завершиться

    public void run() {
        while (!stopped) { doWork(); }
    }
    public void stop() { stopped = true; }
}

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

  • Причина: кэширование и переупорядочивание, нет гарантии видимости.
  • Решение: volatile или синхронизация.
  • Что даёт volatile (видимость, порядок) и чего не даёт (атомарность).

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

  • Считать, что изменение поля всегда сразу видно другим потокам.
  • Использовать volatile для счётчика и терять обновления.

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

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

Показать варианты и ответ
  • ✗Поток с циклом имеет более высокий приоритет и не даёт записи выполниться
  • ✗Изменение поля не успело записаться в память из-за задержки
  • ✗Значение типа boolean нельзя менять из другого потока
  • ✓Нет гарантии видимости — нужен volatile

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

  • Почему volatile не годится для счётчика?
  • Какие ещё конструкции дают гарантию видимости?

Разбор темы

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

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

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

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

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