Что такое race condition?

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

Если два или более потока одновременно обращаются к общему ресурсу (переменной, файлу, объекту) и хотя бы один из них изменяет его, то поведение программы становится непредсказуемым. Это и есть race condition.

Простой пример на Kotlin

var counter = 0
fun increment() {
for (i in 1..1000) {
counter++
}
}
fun main() {
val t1 = Thread { increment() }
val t2 = Thread { increment() }
t1.start()
t2.start()
t1.join()
t2.join()
println("Counter = $counter")
}

Что ожидается:

  • Оба потока по 1000 раз увеличивают счётчик.

  • Значит, counter должен быть 2000.

Что получаем:

  • Иногда 1980, иногда 2000, иногда 1957…

Почему?

Операция counter++ не атомарна:

  1. Считать значение counter.

  2. Увеличить его на 1.

  3. Записать обратно.

→ Если два потока выполняют это одновременно, они могут перезаписать результат друг друга, и часть инкрементов теряется.

📌 Условия возникновения race condition

  1. **Несколько потоков
    **
  2. **Совместный доступ к одному ресурсу
    **
  3. **Хотя бы один поток модифицирует ресурс
    **
  4. **Отсутствие синхронизации
    **

⚠️ Последствия

  • Непредсказуемое поведение.

  • Трудновоспроизводимые баги (иногда проявляются, иногда нет).

  • Повреждение состояния данных.

  • Уязвимости безопасности.

🔐 Как избежать race condition

1. Синхронизация доступа

Использование synchronized:

```python
@Synchronized
fun safeIncrement() {
counter++
}

Или явно:
```python  
<br/><br/>val lock = Any()
synchronized(lock) {
counter++
}

2. Использование атомарных типов

Kotlin/Java:

<br/>val counter = AtomicInteger(0)
counter.incrementAndGet()

3. Изоляция состояния:

    • Каждый поток работает со своим куском данных (например, через ThreadLocal).

4. Фреймворки с акторной моделью:

    • Пример: kotlinx.coroutines.channels или StateFlow в Compose.

📦 Примеры в Android

  • Множественные потоки, изменяющие UI без runOnUiThread.

  • Одновременное сохранение/чтение SharedPreferences.

  • Доступ к SQLite из разных потоков без синхронизации.

  • Race condition между Activity.onDestroy() и результатами сетевых запросов.

🧠 Race condition vs Deadlock

Характеристика Race condition Deadlock
Что происходит Результат зависит от порядка потоков Потоки навсегда блокированы
--- --- ---
Опасность Непредсказуемость, потеря данных Заморозка приложения
--- --- ---
Проявление Случайное, зависит от планировщика ОС Стабильное зависание
--- --- ---
Решение Синхронизация, атомарные операции Контроль порядка блокировок, tryLock
--- --- ---

Итого

Race condition — это критическая ошибка, возникающая, когда несколько потоков одновременно работают с одним ресурсом без правильной координации. Она приводит к случайным багам, потерям данных и нестабильному поведению приложения. Чтобы избежать этого, следует грамотно синхронизировать доступ к общим ресурсам.