Нужна ли синхронизация корутин, запущенными в Main Dispatcher, которые инкрементируют общую переменную

«Нужна ли синхронизация корутин, запущенными в Main Dispatcher, которые инкрементируют общую переменную» — вопрос из категории Coroutines и Flow, который задают на 23% собеседований Android Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, синхронизация нужна, даже при работе в Main Dispatcher. Хотя Main Dispatcher однопоточный, корутины могут приостанавливаться (например, при вызове delay()), что приводит к race condition при изменении общих переменных.

Пример:

var counter = 0

fun main() = runBlocking {
    repeat(100) {
        launch(Dispatchers.Main) {
            val temp = counter
            delay(10) // точка приостановки
            counter = temp + 1
        }
    }
    delay(1000)
    println(counter) // Может быть меньше 100
}

Решение - использовать атомарные типы или мьютексы:

val counter = AtomicInteger(0)
// или
val mutex = Mutex()
var counter = 0

// В корутине:
mutex.withLock {
    counter++
}