Что означает отношение «happens-before» в модели памяти Java (Java Memory Model)?

«Что означает отношение «happens-before» в модели памяти Java (Java Memory Model)?» — вопрос из категории Java Core, который задают на 24% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Happens-before — это формальное отношение, определенное в Java Memory Model (JMM), которое гарантирует видимость и упорядоченность операций памяти между потоками. Если операция A happens-before операция B, то все изменения памяти, сделанные в A, будут видны потоку, выполняющему B.

Почему это важно: Без этих гарантий компилятор и процессор могут переупорядочивать инструкции для оптимизации, что в многопоточном коде может привести к неожиданным результатам (проблемы видимости и порядка).

Ключевые правила happens-before:

  1. Правило монитора (с synchronized / Lock): Разблокировка монитора happens-before последующая блокировка того же монитора.
  2. Правило volatile: Запись в volatile-поле happens-before каждое последующее чтение того же поля.
  3. Правило запуска потока: Вызов thread.start() happens-before любое действие в запущенном потоке.
  4. Правило завершения потока: Все действия в потоке happen-before вызов thread.join() в другом потоке.
  5. Правило транзитивности: Если A happens-before B, а B happens-before C, то A happens-before C.

Пример с volatile:

// Поток 1 (Писатель)
x = 42;          // (1) Обычная запись
ready = true;    // (2) Запись в volatile. (1) happens-before (2) (правило программы)

// Поток 2 (Читатель)
if (ready) {     // (3) Чтение volatile. (2) happens-before (3) (правило volatile)
    // Гарантированно увидит x = 42, потому что (1) happens-before (3) по транзитивности.
    System.out.println(x); // (4)
}

Без модификатора volatile у переменной ready гарантии happens-before между потоками 1 и 2 нет, и поток 2 может увидеть ready == true, но при этом старое значение x = 0.