Gymterview
middle

Что такое race condition?

Состояние гонки (race condition) – это ошибка проектирования многопоточной программы, при которой корректность работы зависит от порядка выполнения потоков. Поведение программы становится недетерминированным: при одном порядке выполнения результат правильный, при другом – ошибочный.

Два типа race condition

Тип Описание Пример
Check-then-act Проверка условия и действие на его основе – не атомарны if (map.containsKey(key)) map.get(key)
Read-modify-write Чтение, изменение и запись значения – не атомарны count++

Пример: check-then-act

Пример: гонка при ленивой инициализации
public class LazyInitRace {
    private static ExpensiveObject instance;

    // НЕ потокобезопасно!
    public static ExpensiveObject getInstance() {
        if (instance == null) {         // Поток A проверяет — null
            // Поток B тоже проверяет — null
            instance = new ExpensiveObject(); // Оба создают экземпляр!
        }
        return instance;
    }
}

Пример: read-modify-write

Пример: гонка при инкременте счётчика
public class CounterRace {
    private int count = 0;

    // НЕ потокобезопасно!
    public void increment() {
        count++; // Три операции: read → modify → write
    }

    // Возможный сценарий при count = 5:
    // Поток A: read count → 5
    // Поток B: read count → 5
    // Поток A: write count → 6
    // Поток B: write count → 6  ← Должно быть 7!
}

Почему race condition опасен

  1. Недетерминированность – баг может проявляться один раз на тысячу запусков, что делает его крайне сложным для воспроизведения.
  2. Зависимость от окружения – на машине разработчика (1 ядро) баг не воспроизводится, а на production-сервере (64 ядра) – постоянно.
  3. Каскадные последствия – повреждение данных, нарушение инвариантов, «невозможные» состояния объектов.

Аналогия из жизни. Два человека одновременно проверяют, есть ли молоко в холодильнике, видят, что нет, и оба идут в магазин. В итоге – два пакета молока вместо одного. «Проверил – пусто» и «пошёл купить» – не атомарная операция.

На собеседовании. Назовите два типа race condition: check-then-act и read-modify-write. Приведите конкретный пример кода. Подчеркните, что race condition проявляется недетерминированно и зависит от таймингов – это делает его одним из самых сложных классов багов. Также упомяните инструменты обнаружения: Thread Sanitizer (для native кода), JCStress (для Java-кода), а также статический анализ (FindBugs/SpotBugs с детектором MT_CORRECTNESS).