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

Какие операции вызывает Happens-Before

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

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

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

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

Happens-Before отношения в Java

Happens-Before — это отношение в Java Memory Model, которое гарантирует видимость изменений между потоками. Если операция A happens-before операции B, то все эффекты операции A будут видны потоку, выполняющему операцию B. Это критически важно для правильной работы многопоточного кода.

Основные Happens-Before отношения

1. Синхронизация через synchronized

Unlock happens-before Lock — выход из синхронизированного блока происходит раньше входа в следующий синхронизированный блок того же объекта:

public class SyncExample {
    private int value = 0;
    
    public synchronized void write() {
        value = 42; // Это изменение будет видно в read()
    }
    
    public synchronized int read() {
        return value; // Гарантированно видим 42
    }
}

2. Volatile переменные

Volatile Write happens-before Volatile Read — запись в volatile переменную происходит раньше чтения из неё другим потоком:

public class VolatileExample {
    private volatile boolean flag = false;
    private int value = 0;
    
    public void writer() {
        value = 42;
        flag = true; // Volatile write
    }
    
    public void reader() {
        if (flag) { // Volatile read
            System.out.println(value); // Гарантированно 42
        }
    }
}

Важно: volatile защищает только саму переменную, но гарантирует видимость предыдущих операций.

3. Запуск и завершение потока

Thread Start happens-before — все действия в главном потоке перед thread.start() видны потоку при его запуске:

public class ThreadStartExample {
    private int value = 0;
    
    public void startThread() {
        value = 42;
        Thread thread = new Thread(() -> {
            System.out.println(value); // Гарантированно 42
        });
        thread.start(); // Happens-before гарантирует видимость
    }
}

Thread Termination happens-before — все действия в потоке завершаются раньше, чем главный поток продолжает работу после thread.join():

public class ThreadJoinExample {
    private int value = 0;
    
    public void joinThread() throws InterruptedException {
        Thread thread = new Thread(() -> {
            value = 42; // Это выполнится раньше join() вернёт управление
        });
        thread.start();
        thread.join(); // Ждём завершения потока
        System.out.println(value); // Гарантированно 42
    }
}

4. Synchronized collection и locks

Lock Release happens-before Lock Acquire — освобождение lock происходит раньше, чем другой поток его захватит:

import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int value = 0;
    
    public void increment() {
        lock.lock();
        try {
            value++;
        } finally {
            lock.unlock(); // Release happens-before
        }
    }
    
    public int getValue() {
        lock.lock();
        try {
            return value; // Видим актуальное значение
        } finally {
            lock.unlock();
        }
    }
}

Happens-Before в Concurrent Collections

ConcurrentHashMap, CopyOnWriteArrayList и другие concurrent коллекции обеспечивают happens-before отношения:

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentMapExample {
    private final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    
    public void writer() {
        map.put("key", 42); // Happens-before для читателей
    }
    
    public void reader() {
        Integer value = map.get("key"); // Видим актуальное значение
        System.out.println(value);
    }
}

Happens-Before для Future и Executor

Submit to Executor happens-before Execution — отправка задачи в executor происходит раньше, чем она выполняется:

import java.util.concurrent.*;

public class ExecutorExample {
    private int value = 0;
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    
    public void submit() {
        value = 42;
        executor.submit(() -> {
            System.out.println(value); // Гарантированно 42
        });
    }
}

Task Completion happens-before Future.get() — завершение задачи происходит раньше, чем метод get() вернёт результат.

Таблица Happens-Before отношений

ОперацияHappens-Before гарантирует
synchronized unlockСледующий synchronized lock
volatile writevolatile read
thread.start()Код потока
Код потокаthread.join()
executor.submit()Выполнение задачи
Завершение задачиFuture.get()
ReentrantLock unlockReentrantLock lock

Практические правила

Для безопасного многопоточного кода:

  • Используйте synchronized для простых случаев
  • Используйте volatile для флагов и простых значений
  • Используйте ReentrantLock для более сложных сценариев
  • Используйте Executor и Future для асинхронных операций
  • Помните, что обычные переменные БЕЗ синхронизации не имеют happens-before гарантий