Зачем нужна модель памяти
На многоядерных процессорах каждый поток может работать с кэшированными версиями данных, а компилятор и процессор вправе переупорядочивать инструкции ради скорости. Без правил один поток мог бы не увидеть запись другого или увидеть её «не в том порядке».
Java Memory Model (JMM) — это контракт, который определяет, какие записи одного потока гарантированно видны другому и в каком порядке. Он абстрагирует железо: пишете по правилам JMM — код корректен на любой платформе.
Три проблемы: видимость, упорядочивание, атомарность
Видимость: увидит ли поток B значение, записанное потоком A. Упорядочивание: в каком порядке B видит несколько записей A. Атомарность: выполняется ли операция неделимо (например, i++ — это read-modify-write, не атомарно).
Это три РАЗНЫХ свойства, и их часто путают. volatile решает видимость и упорядочивание, но НЕ делает составные операции атомарными.
Отношение happens-before
JMM формулируется через happens-before: если действие A happens-before B, то эффект A виден B. Основные источники: порядок внутри одного потока (program order); разблокировка монитора happens-before его последующего захвата; запись volatile happens-before последующего чтения той же переменной; Thread.start() happens-before кода потока; код потока happens-before возврата из join().
Если между двумя обращениями к общим данным нет цепочки happens-before — это гонка данных, и поведение не гарантировано.

Семантика volatile и final
Чтение/запись volatile-поля атомарны (включая long/double) и устанавливают happens-before: всё, что поток записал до записи volatile, становится видно потоку, который затем прочитал это volatile. Поэтому volatile годится для флагов и публикации ссылок, но не для счётчиков (count++ — не атомарно; нужны Atomic-классы или synchronized).
Корректно инициализированные final-поля имеют гарантию безопасной публикации: другой поток, получивший ссылку на объект, увидит правильно установленные final-поля без дополнительной синхронизации. Это основа неизменяемых объектов как потокобезопасных.
volatile boolean running = true; // флаг остановки — корректно
void stop() { running = false; } // видно другому потоку
// но: volatile int n; n++; — НЕ атомарно (read-modify-write)
Как отвечать на собеседовании
Разделите видимость/упорядочивание/атомарность, объясните happens-before и его источники (volatile, monitor lock, start/join), и точно сформулируйте volatile: даёт видимость и запрет переупорядочивания, но не атомарность составных операций (для счётчиков — Atomic/synchronized). Добавьте про безопасную публикацию через final — это уровень, на котором видно глубокое понимание конкурентности.