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

Какие знаешь события, связанные отношением Happens Before?

2.4 Senior🔥 111 комментариев
#JVM и управление памятью#Многопоточность

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

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

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

Happens-Before (HB) отношение в Java

Happens-Before — это ключевая концепция Java Memory Model (JMM), которая определяет порядок видимости изменений данных между потоками. Это гарантия того, что результаты одного потока будут видны другому потоку в определённом порядке.

Что означает Happens-Before?

Если операция A happens-before операции B, это означает:

  • Все изменения памяти, произведённые A, будут видны потоку, выполняющему B
  • Порядок операций A и B будет сохранён относительно друг друга (для видимости)

Основные правила Happens-Before

1. Program Order (Порядок в программе)

Если действие X происходит раньше Y в одном потоке, то X happens-before Y:

int a = 1;        // Действие 1
int b = a + 1;    // Действие 2 (happens-before Действия 1)
System.out.println(b); // Действие 3 (happens-before Действия 2)

2. Monitor Lock (Блокировка через synchronized)

Ужимание блокировки happens-before получение той же блокировки другим потоком:

class Counter {
    private int value = 0;
    
    synchronized void increment() {
        value++; // Действие 1
    }            // Освобождение блокировки (unlock)
    
    synchronized int getValue() {
        // Получение блокировки (lock) happens-before чтение value
        return value; // Действие 2 (гарантированно видит increment)
    }
}

Counter counter = new Counter();
Thread t1 = new Thread(() -> counter.increment()); // t1 выполняет increment
Thread t2 = new Thread(() -> System.out.println(counter.getValue())); // t2 получит увеличенное значение
t1.start();
t1.join(); // Ждём завершения t1
t2.start();

3. Volatile (Видимость изменений)

Запись в volatile переменную happens-before чтения этой переменной:

class Data {
    private volatile boolean ready = false;
    private int value = 0;
    
    void prepare() {
        value = 42;     // Действие 1
        ready = true;   // Действие 2 (запись в volatile)
    }
    
    void consume() {
        while (!ready) {  // Чтение volatile (happens-after записи)
            Thread.onSpinWait();
        }
        System.out.println(value); // Гарантированно выведет 42!
    }
}

Без volatile другой поток может не увидеть value = 42.

4. Thread Start (Запуск потока)

Указания в main потоке перед thread.start() happens-before выполнение кода в новом потоке:

int x = 5;
Thread t = new Thread(() -> {
    System.out.println(x); // Гарантированно выведет 5
});
t.start();

5. Thread Termination (Завершение потока)

Все действия в потоке happens-before thread.join() вернёт управление:

int[] result = new int[1];
Thread t = new Thread(() -> {
    result[0] = compute(); // Действие в потоке
});
t.start();
t.join(); // Все изменения в t видны здесь
System.out.println(result[0]); // Гарантированно получит результат

6. Interruption (Прерывание потока)

Чтение сигнала interrupt via Thread.isInterrupted() happens-after соответствующий thread.interrupt():

Thread t = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        // Выполняем работу
    }
});
t.start();
Thread.sleep(100);
t.interrupt(); // Сигнал к прерыванию
t.join(); // Гарантировано увидит interrupt

7. Action в finalizer happens-before finalizer вызова

Код в finalize() happens-before сборка мусора для другого объекта.

Примеры: Трансмиссия через HB

Пример 1: Неправильный код (data race)

class UnsafeSharing {
    private int value = 0;
    
    void writer() {
        value = 100; // Может не быть видно другому потоку!
    }
    
    void reader() {
        System.out.println(value); // Может вывести 0 или 100?
    }
}

Нет happens-before между writer() и reader() → data race!

Пример 2: Исправленный код (synchronized)

class SafeSharing {
    private int value = 0;
    
    synchronized void writer() {
        value = 100; // unlock happens-before lock в reader
    }
    
    synchronized void reader() {
        System.out.println(value); // Гарантированно 100
    }
}

Пример 3: Исправленный код (volatile)

class SafeSharingVolatile {
    private volatile int value = 0;
    
    void writer() {
        value = 100; // volatile write
    }
    
    void reader() {
        System.out.println(value); // volatile read happens-after write
    }
}

Synchronization between Threads (Синхронизация)

Happens-Before создаёт синхронизацию между потоками через:

// Пример: Two-phase commit
class TwoPhase {
    private volatile boolean phase1Done = false;
    private volatile boolean phase2Done = false;
    
    void phase1() {
        // работа
        phase1Done = true; // volatile write
    }
    
    void phase2() {
        while (!phase1Done) { } // volatile read (happens-after write)
        // работа
        phase2Done = true;
    }
}

Combinatorial Effect (Комбинаторное действие)

HB отношения могут транзитивно комбинироваться:

// A hb B AND B hb C => A hb C
int x = 0;           // A
synchronized (lock) {
    x = 1;          // B (inside monitor)
}                    // unlock
synchronized (lock) {
    int y = x;      // C (after lock, видит x=1)
}

Практическое значение для разработчика

  1. Используй synchronized/volatile для data sharing между потоками
  2. Избегай data races — когда два потока обращаются к одному полю без синхронизации
  3. Используй высокоуровневые конструкты: AtomicInteger, CountDownLatch, CyclicBarrier, Semaphore
  4. Помни о HB при использовании java.util.concurrent пакета
// Правильно: CountDownLatch использует HB
CountDownLatch latch = new CountDownLatch(1);
Thread t = new Thread(() -> {
    // работа
    latch.countDown(); // HB
});
t.start();
latch.await(); // happens-after countDown()

Happens-Before — это основа потокобезопасности в Java. Понимание этих правил критично для написания корректного многопоточного кода.

Какие знаешь события, связанные отношением Happens Before? | PrepBro