Что происходит при использовании synchronized внутри корутины
Что проверяют
Понимаете ли вы, что корутина это не поток, и что монитор принадлежит потоку. Вопрос быстро разделяет тех, кто просто пользуется launch, и тех, кто представляет, как корутины ложатся на пул потоков.
Как отвечать
synchronized берёт монитор объекта для текущего потока и блокирует именно поток. Внутри корутины это означает две вещи.
Первая: пока секция выполняется, поток диспетчера занят и не может обслуживать другие корутины. У Dispatchers.Default размер пула примерно равен числу ядер, поэтому несколько таких секций, особенно с ожиданием сети или диска, останавливают всё приложение, а на главном потоке это прямой путь к ANR.
Вторая: внутри synchronized нельзя вызвать suspend-функцию, компилятор это запрещает. Причина в том, что после приостановки корутина может продолжиться на другом потоке, а освобождать монитор обязан тот же поток, который его взял.
Правильный инструмент это Mutex из kotlinx.coroutines:
private val mutex = Mutex()
private var count = 0
suspend fun increment() = mutex.withLock { count++ }Он приостанавливает корутину вместо блокировки потока. Рядом назовите альтернативы: AtomicInteger для счётчика, MutableStateFlow.update для состояния, ограничение изменяемых данных одним диспетчером. Оговорка для честности: короткая чисто вычислительная секция под synchronized не преступление, вред приносит блокировка на ожидании.
Где спотыкаются
Считают, что synchronized защищает корутины друг от друга. Он разводит потоки, а две корутины на одном потоке в критической секции пересечься и так не могут; после приостановки владелец монитора перестаёт совпадать с тем, кто исполняет код.
Второе: заменяют synchronized на Mutex и получают зависание на вложенном захвате. Mutex не реентрантный: повторный withLock из кода, который уже держит замок, будет ждать вечно.
Третье: обходят запрет на приостановку через runBlocking внутри блока. Так поток блокируется гарантированно, а если внутри ждут корутину с того же ограниченного диспетчера, получается дедлок.