Как потоки в Java обеспечивают видимость изменений переменной в общей памяти?

«Как потоки в Java обеспечивают видимость изменений переменной в общей памяти?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Без синхронизации изменения переменной, сделанные одним потоком, могут остаться невидимыми для других потоков из-за кэширования в регистрах CPU или локальной памяти потока.

Механизмы обеспечения видимости (happens-before):

  • volatile — гарантирует, что чтение переменной всегда увидит последнее записанное значение.
  • synchronized (блоки или методы) — освобождение монитора (unlock) happens-before последующее его захват (lock), обеспечивая видимость всех изменений.
  • final поля — гарантированно видны другим потокам после корректного завершения конструктора.

Пример проблемы и решения:

class Example {
    // private int x = 0; // Проблема: изменение может быть не видно
    private volatile int x = 0; // Решение: видимость гарантирована
    void writer() { x = 1; }
    void reader() { System.out.println(x); }
}

Без volatile метод reader() может вывести 0, даже если writer() уже выполнился.