Какие проблемы возникают при отсутствии синхронизации доступа к переменным
Что проверяют
Знаете ли вы, что проблем тут три: атомарность, видимость и порядок операций, и что volatile решает только часть. Следом обычно спрашивают, как защищать состояние в корутинах, где synchronized вокруг suspend-вызова не работает.
Как отвечать
- потерянные обновления:
counter++это чтение, сложение и запись. Два потока читают одно значение, оба записывают своё, одно увеличение пропадает; - видимость: без отношения happens-before по модели памяти Java поток может сколь угодно долго не видеть запись другого потока. Классика: флаг
isRunningбез@Volatile, и фоновый цикл не останавливается; - небезопасная публикация: из-за переупорядочивания другой поток может получить ссылку на объект раньше, чем увидит значения его полей;
- сломанные коллекции:
ArrayListиHashMapпри параллельной записи теряют элементы и портят структуру, перебор падает сConcurrentModificationException.
Инструменты по задаче:
@Volatile: видимость одного поля, для флагов;AtomicInteger,AtomicReference: атомарные операции без блокировок;synchronizedиReentrantLock: составные инварианты в потоковом коде;Mutexиз kotlinx.coroutines: приостанавливает корутину, а не блокирует поток;MutableStateFlow.update { }: атомарное изменение через CAS, в отличие отstate.value = state.value + 1;- один поток на состояние: менять данные только на
Dispatchers.Mainили наDispatchers.Default.limitedParallelism(1).
На Android добавьте правило главного потока: состояние, которое читает UI, меняют с главного потока, а из фона используют postValue у LiveData или withContext(Dispatchers.Main).
Где спотыкаются
Ставят @Volatile на счётчик и считают, что count++ стал безопасным. Видимость появилась, атомарности нет.
Называют дедлок последствием отсутствия синхронизации. Взаимная блокировка возникает от блокировок, взятых в разном порядке, а без синхронизации получают гонки.
Думают, что корутины одного scope выполняются по очереди. Dispatchers.Default и Dispatchers.IO раздают работу нескольким потокам, и общая переменная без защиты ломается так же, как с голыми Thread.