Вопрос проверяет знание формальных правил Java Memory Model, которые задают гарантии видимости и порядка между потоками.
Отношение happens-before формируется набором строго определённых правил.
К ним относятся синхронизация через synchronized, volatile, запуск и завершение потоков.
Также учитываются операции с final полями и транзитивность.
Эти правила определяют, какие изменения памяти гарантированно видны.
Без них многопоточный код не имеет формальной корректности.
Java Memory Model задаёт набор операций, которые формируют отношение happens-before.
Перед перечислением важно отметить, что правила являются формальными гарантиями, а не рекомендациями.
Операции в одном потоке:
Выполняются в программном порядке.
Предыдущая операция happens-before последующей.
synchronizedДля одного и того же монитора:
Выход из synchronized-блока happens-before
Входа в synchronized-блок другим потоком.
synchronized (lock) {
shared = 1;
}
// happens-before
synchronized (lock) {
System.out.println(shared);
}
volatileДля одного поля:
Запись в volatile happens-before
Любого последующего чтения этого же поля.
Вызов:
thread.start();
happens-before:
всем действиям внутри нового потока.
Завершение потока happens-before:
успешному возврату из join().
final поляЗапись final поля в конструкторе happens-before:
чтению этого поля после корректной публикации объекта.
Если:
A happens-before B
B happens-before C
То:
A happens-before C
Отношение happens-before формируется через synchronized, volatile, жизненный цикл потоков, final поля и транзитивность, задавая формальную основу многопоточности.