Какие методы синхронизации существуют в Kotlin и корутинах?
Что проверяют
Понимаете ли вы разницу между блокировкой потока и приостановкой корутины. Кандидат, который тащит в корутины synchronized и ReentrantLock, не представляет, чем занят пул диспетчера. Следом спрашивают, как защитить состояние во вьюмодели.
Как отвечать
Разделите два мира. Блокирующие примитивы JVM (synchronized, ReentrantLock, java.util.concurrent) останавливают поток целиком. В корутинах поток общий и дорогой, поэтому нужны suspend-аналоги из kotlinx.coroutines:
Mutex:withLock { }, при занятом локе корутина приостанавливается, а не занимает поток. Не реентерабельный: повторный вход из той же корутины даёт дедлок;Semaphore:withPermit { }, ограничивает число одновременных операций, например параллельных запросов в сеть;Channel: передача данных вместо разделяемого состояния, по умолчаниюRENDEZVOUS, естьBUFFERED,CONFLATEDиUNLIMITED;MutableStateFlow:update { }меняет значение атомарно через CAS, для состояния экрана это чаще верный выбор, чем мьютекс;- атомарные типы:
java.util.concurrent.atomicна JVM,atomicfuв мультиплатформенном коде; CompletableDeferred: ожидание результата, который придёт из другого места,await()приостанавливает вызывающего;- ограничение параллелизма:
Dispatchers.Default.limitedParallelism(1)даёт последовательное исполнение, при котором синхронизация не нужна.
Про actor скажите, что он есть, но помечен @ObsoleteCoroutinesApi и в новом коде не нужен. Добавьте про структурированную конкурентность: coroutineScope, async с await и Job.join() закрывают половину задач «дождаться результата» без локов.
Где спотыкаются
Пытаются обернуть suspend-вызов в synchronized. Компилятор это не пропустит, а обход через блокирующий лок занимает поток из пула Dispatchers.Default, где потоков всего по числу ядер.
Считают, что смена диспетчера сама по себе защищает данные. Код на Dispatchers.Main последовательный, но любой withContext внутри разрывает атомарность.
Берут Mutex там, где хватило бы update { }. Мьютексы расползаются по классам, а первый же вложенный вызов защищённого метода даёт дедлок.