Какую проблему решает Mutex в многопоточном программировании?

«Какую проблему решает Mutex в многопоточном программировании?» — вопрос из категории Алгоритмы и структуры данных, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Mutex (Mutual Exclusion — взаимное исключение) — это примитив синхронизации, предназначенный для решения проблемы гонки данных (race condition) при доступе нескольких потоков к общему ресурсу (переменной, структуре данных, файлу, устройству).

Решаемая проблема: Без синхронизации два потока могут читать и модифицировать общие данные одновременно, что приводит к непредсказуемому и некорректному результату.

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

Пример проблемы (race condition) и решения с помощью ReentrantLock в Java:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Counter {
    private int value = 0;
    private final Lock lock = new ReentrantLock(); // Создаем мьютекс

    // НЕПРАВИЛЬНО: Без синхронизации
    public void unsafeIncrement() {
        value++; // Операция не атомарна (чтение-изменение-запись)
    }

    // ПРАВИЛЬНО: Использование мьютекса для защиты критической секции
    public void safeIncrement() {
        lock.lock(); // Захватываем блокировку
        try {
            value++; // Критическая секция
        } finally {
            lock.unlock(); // Всегда освобождаем блокировку в finally
        }
    }

    public int getValue() {
        return value;
    }
}

Преимущества ReentrantLock перед synchronized:

  • tryLock(): Попытка захвата блокировки с таймаутом или без, что позволяет избежать deadlock.
  • lockInterruptibly(): Захват блокировки с возможностью прерывания потока.
  • Честность (fairness): Можно создать "честный" мьютекс, который отдает блокировку самому долго ожидающему потоку.
  • Поддержка нескольких Condition: Более гибкое управление ожиданием потоков.

Ключевое правило: Критическая секция под блокировкой должна быть как можно короче, чтобы минимизировать contention (соревнование потоков).