Java for Beginner
680 subscribers
573 photos
158 videos
12 files
869 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Java Memory Model (JMM)

Java Memory Model (JMM) определяет, как изменения в памяти, производимые одним потоком, становятся видимыми другим потокам. JMM описывает:
Как потоки взаимодействуют с основной памятью.
Правила синхронизации доступа к переменным.
Гарантии порядка операций, чтобы избежать непредсказуемого поведения в многопоточном программировании.
Главной целью
JMM является устранение несогласованности данных и определение поведения программы на уровне видимости и порядка выполнения операций в многопоточной среде.

Основные концепции JMM

Главная (основная) память (Main Memory):
Это область памяти, где хранятся все общие переменные (поля классов, статические переменные и т. д.).
Каждому потоку также выделяется собственная локальная память (Thread Local Memory), где временно сохраняются копии значений переменных.


Кэширование и локальная память потоков:
Потоки могут не сразу обращаться к основной памяти. Вместо этого они используют кэш или локальную копию переменных.
Изменения, сделанные в локальной памяти одного потока, могут не быть видны другим потокам, если они не будут синхронизированы с основной памятью.


Гарантии видимости и упорядочение:
Видимость означает, что если один поток изменил переменную, другой поток сможет увидеть это изменение.
Упорядочение операций гарантирует, что инструкции выполняются в предсказуемом порядке.


Инструкции и пересортировка:
Компилятор и процессор могут менять порядок выполнения операций для оптимизации.
Например, две независимые операции могут поменяться местами, если это ускорит выполнение программы.
JMM устанавливает правила, чтобы такая пересортировка не нарушала корректность многопоточной программы.

#Java #Training #Medium #JMM
Правила JMM для взаимодействия потоков

JMM включает несколько базовых правил для обеспечения корректного поведения программы:

Правило Happens-Before:
Если одна операция происходит "раньше" другой с точки зрения JMM, это означает, что все изменения, сделанные первой операцией, будут видны второй.
Например, вызов Thread.start() имеет отношение happens-before к всем действиям внутри нового потока.


Правило синхронизации (synchronized блоки):
Использование ключевого слова synchronized гарантирует, что все изменения, сделанные внутри блока, будут видны другим потокам после выхода из него.
class Counter {
private int count = 0;

public synchronized void increment() {
count++; // Этот блок синхронизирован, изменения видны другим потокам.
}

public synchronized int getCount() {
return count;
}
}


Volatile-переменные:

Использование ключевого слова volatile указывает, что изменения в этой переменной немедленно записываются в основную память.
volatile гарантирует видимость, но не упорядочение операций.

class FlagExample {
private volatile boolean flag = false;

public void setFlagTrue() {
flag = true;
}

public void checkFlag() {
if (flag) {
System.out.println("Flag is true!");
}
}
}


Конструкторы и финализация:
Все операции, выполненные в конструкторе, должны быть завершены до того, как ссылка на объект станет доступной другим потокам.
Это правило предотвращает "полуинициализированные" объекты в многопоточной среде.
Внутреннее устройство
JMM и управление памятью

На уровне устройства, JMM опирается на низкоуровневую архитектуру процессора и системы памяти:

Слабая консистентность памяти:
Современные процессоры и системы используют слабую консистентность, где чтение и запись могут происходить асинхронно для оптимизации.
JMM компенсирует это ограничение, обеспечивая строгие правила синхронизации.

Барьеры памяти (Memory Barriers):
JMM вводит так называемые барьеры памяти (memory barriers или fences) для обеспечения правильного порядка операций.
Существуют два типа барьеров:
Барьеры записи (Write Barrier) — гарантируют, что все предшествующие записи переменных видны другим потокам.
Барьеры чтения (Read Barrier) — гарантируют, что все последующие операции чтения переменных происходят после предыдущих операций записи.


Пересортировка (Reordering):
Компилятор и процессор могут изменять порядок инструкций для повышения производительности.
int x = 0, y = 0;
x = 1; // A
y = x + 1; // B
// Компилятор может поменять A и B местами, если это ускоряет выполнение.


Final-поля:
Поля, объявленные как final, имеют особые гарантии: они инициализируются до того, как объект станет доступен другим потокам.
Это предотвращает утечки ссылок на не полностью инициализированные объекты.


Пример нарушения JMM
Рассмотрим типичную проблему гонки данных:
class SharedObject {
private int counter = 0;

public void increment() {
counter++; // Несинхронизированный доступ к переменной.
}

public int getCounter() {
return counter;
}
}
Если несколько потоков одновременно вызывают метод increment, результат может быть непредсказуемым из-за отсутствия синхронизации: один поток может перезаписать значение, измененное другим потоком.


Правильное использование JMM

Для обеспечения корректности программы в многопоточной среде можно использовать следующие механизмы:

Использование synchronized для метода increment():
public synchronized void increment() {
counter++;
}
Применение AtomicInteger для безопасного инкремента:

java
Копировать код
import java.util.concurrent.atomic.AtomicInteger;

class SharedObject {
private AtomicInteger counter = new AtomicInteger(0);

public void increment() {
counter.incrementAndGet(); // Безопасная атомарная операция.
}

public int getCounter() {
return counter.get();
}
}


#Java #Training #Medium #JMM