Может ли возникнуть проблема гонки потоков при изменении общего ресурса
Что проверяют
Понимаете ли вы, откуда берётся гонка и чем видимость отличается от атомарности. На Android вопрос почти всегда уходит в корутины: как защищать общее состояние, если код работает на Dispatchers.Default. Следом просят исправить конкретный код со счётчиком или со StateFlow.
Как отвечать
Да. Гонка возникает, когда несколько потоков работают с общими данными, хотя бы один пишет, и между операциями нет синхронизации. Классический пример count++: это три шага (прочитать, прибавить, записать), и два потока легко теряют одно из увеличений. Сюда же относится «проверил, потом действую»: if (!map.containsKey(k)) map[k] = v из двух потоков.
Инструменты по нарастанию:
@Volatile: даёт видимость записи другим потокам, но не атомарность.count++по полю с@Volatileпо-прежнему гонка;- атомики:
AtomicInteger.incrementAndGet(),AtomicReference.updateAndGet {}для одной переменной без блокировок; synchronizedиReentrantLock: для составных операций;- потокобезопасные коллекции:
ConcurrentHashMapсcomputeIfAbsentвместо проверки и вставки двумя вызовами; - корутины: внутри
synchronizedнельзя вызвать suspend-функцию, поэтому берутMutexиз kotlinx.coroutines иmutex.withLock {}или ограничивают работу одним потоком черезDispatchers.Default.limitedParallelism(1); StateFlow: запись_state.value = _state.value.copy(...)не атомарна, а_state.update { it.copy(...) }повторяет попытку через сравнение и замену.
Лучшая защита: не делить изменяемое состояние вовсе. Держите его в одном месте, например во ViewModel, меняйте только с Dispatchers.Main и отдавайте наружу неизменяемые копии.
Где спотыкаются
Ставят @Volatile и считают проблему решённой. Потерянные обновления никуда не делись.
Думают, что корутины избавляют от гонок. На Dispatchers.Default и Dispatchers.IO они выполняются параллельно на пуле потоков, и общая var ломается так же, как с обычными потоками.
Проверяют исправление одним ручным запуском. Гонка проявляется редко, поэтому нужен нагрузочный тест: тысячи корутин на Dispatchers.Default и сравнение итоговой суммы с ожидаемой.