Gymterview
middle

Существует ли способ решения проблемы race condition?

Да, существует несколько способов решения. Выбор зависит от конкретного сценария.

Основные способы

1. Синхронизация (synchronized)

Операции над разделяемым ресурсом помещаются в synchronized блок, гарантирующий взаимное исключение и видимость:

Пример
public class SafeCounter {
    private int count = 0;

    public synchronized void increment() {
        count++; // Теперь атомарно
    }

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

2. Атомарные переменные

Классы java.util.concurrent.atomic обеспечивают атомарность без блокировок:

Пример
private final AtomicInteger count = new AtomicInteger(0);

public void increment() {
    count.incrementAndGet(); // Атомарная CAS-операция
}

3. volatile + однократная запись

Если переменная записывается одним потоком, а читается многими, достаточно volatile:

Пример
private volatile boolean ready = false;

// Поток-писатель
public void prepareData() {
    data = loadData();
    ready = true; // Все потоки-читатели увидят это
}

4. Потокобезопасные коллекции

Использование конкурентных коллекций из java.util.concurrent:

Пример
// Вместо HashMap + synchronized
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.computeIfAbsent("key", k -> expensiveComputation(k)); // Атомарная операция

5. Неизменяемые объекты (Immutable objects)

Объекты, состояние которых нельзя изменить после создания, свободны от race condition по определению:

Пример
public record Point(int x, int y) { }
// Нет сеттеров, нет мутабельного состояния — нет гонок

6. ThreadLocal

Если каждому потоку нужна своя копия переменной:

Пример
private static final ThreadLocal<SimpleDateFormat> formatter =
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

Сводная таблица

Способ Блокировка Подходит для Производительность
synchronized Да Сложные составные операции Средняя
ReentrantLock Да Составные операции с таймаутом, tryLock Средняя
Atomic* Нет (lock-free) Простые read-modify-write Высокая
volatile Нет Одна запись — много чтений Высокая
Concurrent-коллекции Частично Конкурентный доступ к коллекциям Высокая
Immutable objects Нет Передача данных между потоками Высокая
ThreadLocal Нет Изоляция данных по потокам Высокая

Аналогия из жизни. Проблема: два человека одновременно редактируют один документ. Решения: (1) synchronized – очередь к одному принтеру, (2) Atomic – каждый исправляет только своё слово атомарно, (3) Immutable – никто не редактирует, а создаёт новую версию, (4) ThreadLocal – у каждого своя копия документа.

На собеседовании. Не ограничивайтесь одним способом – покажите, что знаете несколько подходов и умеете выбирать: для простого флага – volatile, для счётчика – AtomicInteger, для сложной логики – synchronized или ReentrantLock. Подчеркните, что лучший способ избежать гонок – правильное проектирование: минимизация разделяемого состояния и использование неизменяемых объектов.