← Назад к вопросам

Что такое модель памяти в Java?

1.8 Middle🔥 161 комментариев
#JVM и управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Модель памяти в 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 — комбинация видимости и атомарности