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

Из каких частей состоит JMM

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

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

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

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

# Java Memory Model (JMM)

Определение

Java Memory Model — это спецификация, которая определяет, как потоки взаимодействуют с памятью. JMM гарантирует предсказуемое и безопасное поведение многопоточных Java программ, независимо от аппаратной архитектуры и оптимизаций компилятора.

Главные компоненты JMM

1. Локальная память потока (Thread-Local Memory)

Каждый поток имеет свою локальную память, где хранятся:

  • Копии значений переменных
  • Состояние выполнения
  • Локальные переменные методов

Этот механизм позволяет потокам работать эффективнее, не обращаясь каждый раз к общей памяти.

2. Главная память (Main Memory / Shared Memory)

Общая память, доступная всем потокам:

  • Значения полей объектов
  • Элементы массивов
  • Статические переменные

Все потоки взаимодействуют через главную память. Это создаёт потенциальные проблемы с видимостью данных.

3. Операции памяти (Memory Operations)

JMM определяет четыре базовые операции:

ТОК 1                      ГЛАВНАЯ ПАМЯТЬ              ТОК 2
┌─────────────┐            ┌──────────────┐          ┌─────────────┐
│ Локальная   │  read()    │              │  read()  │ Локальная   │
│ память      ├───────────→│ Переменные   │←─────────┤ память      │
│ потока 1    │  write()   │ объектов     │  write() │ потока 2    │
└─────────────┘            │              │          └─────────────┘
                           └──────────────┘

Четыре операции:

  1. read — чтение значения из главной памяти в локальную
  2. write — запись значения из локальной памяти в главную
  3. load — загрузка значения в переменную из локальной памяти
  4. store — сохранение значения переменной в локальную память

4. Happens-Before Отношения

Это фундаментальное понятие JMM, определяющее порядок операций памяти:

int a = 1;          // Операция 1
int b = a + 1;      // Операция 2 (happens-before операции 1)
System.out.println(b);  // Операция 3

Гарантии happens-before:

a) Program Order

int x = 5;
int y = x + 10;  // y зависит от x (happens-before)

b) Monitor Lock

synchronized(lock) {  // unlock happens-before следующий lock
    count++;
}
synchronized(lock) {
    System.out.println(count);  // Видим изменение
}

c) Volatile Variable

private volatile int count = 0;

public void increment() {
    count++;  // write happens-before следующему read
}

d) Thread Start

int value = 5;
new Thread(() -> {
    System.out.println(value);  // Видим value = 5
}).start();

e) Thread Termination

Thread thread = new Thread(() -> {
    value = 10;
});
thread.start();
thread.join();  // После join() видим value = 10

5. Видимость (Visibility)

Проблема: без гарантий JMM один поток может не увидеть изменения другого потока.

public class VisibilityProblem {
    private int flag = 0;  // БЕЗ volatile
    
    public void thread1() {
        flag = 1;  // Может остаться в локальной памяти потока
    }
    
    public void thread2() {
        if (flag == 1) {  // Может читать старое значение 0
            System.out.println("Flag is 1");
        }
    }
}

Решение с volatile:

private volatile int flag = 0;  // Гарантирует видимость

6. Атомарность (Atomicity)

Некоторые операции неделимы:

private volatile long count = 0;

public void increment() {
    count++;  // НЕ атомарна! Это read-modify-write
}

public synchronized void safeIncrement() {
    count++;  // Теперь атомарна благодаря synchronized
}

public void atomicIncrement() {
    AtomicLong count = new AtomicLong(0);
    count.incrementAndGet();  // Атомарна
}

7. Упорядочивание (Ordering)

Без синхронизации компилятор может переупорядочить операции:

int a = 1;
int b = 2;
int c = a + b;  // Может быть переупорядочено

// С volatile гарантирован порядок
volatile int flag;
int x = 5;
flag = 1;  // Всё перед этой строкой выполнится до неё

Инструменты для работы с JMM

1. Synchronized

synchronized void criticalSection() {
    // Один поток в раз
}

2. Volatile

private volatile boolean done = false;

3. Atomic классы

AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();

4. Locks

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // Критическая секция
} finally {
    lock.unlock();
}

5. Concurrent Collections

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

Практический пример правильного использования

public class SafeCounter {
    private volatile int count = 0;
    private final Object lock = new Object();
    
    public void increment() {
        synchronized(lock) {
            count++;  // Гарантирована видимость и атомарность
        }
    }
    
    public int getCount() {
        return count;  // Видим последнее значение
    }
}

Ключевые выводы

  • Локальная память оптимизирует производительность
  • Главная память обеспечивает синхронизацию между потоками
  • Happens-before определяет порядок операций
  • Видимость обеспечивается через synchronized, volatile и AtomicX
  • JMM - это гарант корректности многопоточного кода

Понимание JMM критично для написания безопасного многопоточного Java кода.