Ключевое слово synchronized
используется для контроля доступа нескольких потоков к общим ресурсам, предотвращая race conditions (состояния гонки) и обеспечивая потокобезопасность.
Монитор объекта
Два способа использования:
Синхронизированный метод:
@Synchronized
fun updateCounter() {
counter++ // Потокобезопасная операция
}
Синхронизированный блок:
fun updateUser(user: User) {
synchronized(this) { // Явное указание объекта блокировки
// Критическая секция
user.balance += amount
}
}
Изменение общих данных:
class Repository {
private val cache = mutableMapOf<String, Data>()
private val lock = Object() // Специальный объект для блокировки
fun putData(key: String, value: Data) {
synchronized(lock) {
cache[key] = value
}
}
}
Работа с Singleton (ленивая инициализация):
class Singleton private constructor() {
companion object {
@Volatile private var instance: Singleton? = null
fun getInstance(): Singleton {
return instance ?: synchronized(this) {
instance ?: Singleton().also { instance = it }
}
}
}
}
Накладные расходы:
Оптимизации:
@Volatile
или Atomic
классыReentrantLock
для сложных сценариевMutex (из kotlinx.coroutines):
private val mutex = Mutex()
suspend fun safeUpdate() {
mutex.withLock {
// Критическая секция
}
}
Атомарные переменные:
private val atomicCounter = AtomicInteger(0)
fun increment() {
atomicCounter.incrementAndGet() // Без блокировок
}
Взаимная блокировка (Deadlock):
// Поток 1:
synchronized(lockA) {
synchronized(lockB) { ... }
}
// Поток 2:
synchronized(lockB) {
synchronized(lockA) { ... } // Возможен deadlock
}
Избыточная синхронизация:
synchronized
— это мощный инструмент для обеспечения потокобезопасности, но он требует аккуратного использования. В современных Android-приложениях предпочтение часто отдается корутинам с Mutex или потокобезопасным коллекциям из пакета java.util.concurrent
.