Что такое модель памяти в Java?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Модель памяти в Java (Java Memory Model)
Что такое JMM
Модель памяти в Java (Java Memory Model, JMM) — это набор гарантий согласованности памяти между потоками. Она определяет, как изменения переменных в одном потоке становятся видны другим потокам.
Каждый процессор имеет собственный кеш, и без явных правил разные потоки могут видеть разные значения одной переменной. JMM решает эту проблему.
Основные концепции
1. Happens-before отношения
Это гарантия, что операция A точно выполнится до операции B и изменения будут видны:
public class HappensBeforeExample {
private int x = 0;
private volatile boolean flag = false;
public void writer() {
x = 42; // 1. Запись в x
flag = true; // 2. Запись в volatile флаг
}
public void reader() {
while (!flag) { // Читаем volatile
// Ждём
}
System.out.println(x); // ГАРАНТИРОВАННО 42, не 0
}
}
Важная гарантия: запись в volatile переменную happens-before чтение этой переменной.
2. Volatile переменные
Volatile гарантирует:
- Чтение/запись переменной всегда из основной памяти (не из кеша)
- Порядок операций соблюдается (не переупорядочиваются)
public class VolatileExample {
private volatile int counter = 0; // Видна всем потокам сразу
public void increment() {
counter++; // Каждый поток видит актуальное значение
}
public int getCounter() {
return counter; // Читаем из памяти, не из кеша
}
}
Но volatile не атомарна! counter++ — это 3 операции (read, modify, write).
3. Синхронизация (Synchronized)
Synchronized блок создаёт memory barrier — барьер памяти:
public class SynchronizedMemory {
private int x = 0;
public synchronized void writer() {
x = 42;
// При выходе из synchronized: все изменения записываются в память
}
public synchronized int reader() {
// При входе: все значения перечитываются из памяти
return x; // ГАРАНТИРОВАННО 42
}
}
Гарантии synchronized:
- Разблокировка happens-before блокировка того же монитора
- Внутри — эксклюзивный доступ (видимость + атомарность)
4. Final переменные
Final создаёт гарантию безопасной инициализации:
public class FinalFieldExample {
private final int x;
private final String name;
public FinalFieldExample(int x, String name) {
this.x = x; // Запись в final
this.name = name; // Запись в final
// После конструктора: гарантированно видны другим потокам
}
}
// Другой поток может безопасно читать:
FinalFieldExample obj = new FinalFieldExample(42, "test");
System.out.println(obj.x); // ГАРАНТИРОВАННО 42
System.out.println(obj.name); // ГАРАНТИРОВАННО "test"
Проблема: Race Condition без гарантий
public class UnsafeCounter {
private int count = 0; // Не volatile!
public void increment() {
count++; // Потокоопасно ДЕ!
}
public int getCount() {
return count; // Может вернуть устаревшее значение
}
}
// Два потока одновременно
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.getCount()); // Может быть < 2000!
Правильное решение
public class SafeCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // Атомарная операция
}
public int getCount() {
return count.get();
}
}
Порядок инструкций (Instruction Reordering)
Bez гарантий JMM компилятор может переупорядочить операции:
public class ReorderingProblem {
static int x = 0, y = 0;
static int r1 = 0, r2 = 0;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
x = 1; // Может выполниться ДО ниже
r1 = y; // из-за переупорядочения
});
Thread t2 = new Thread(() -> {
y = 1;
r2 = x;
});
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("r1=" + r1 + ", r2=" + r2);
// Может быть r1=0 и r2=0! Даже если оба потока выполнились!
}
}
// Решение: volatile
static volatile int x = 0, y = 0;
Итоги
- JMM — контракт согласованности памяти между потоками
- Volatile — видимость, без атомарности
- Synchronized — видимость + взаимное исключение
- Final — безопасная инициализация
- Happens-before — гарантии порядка выполнения
- AtomicXxx — комбинация видимости и атомарности